Skip to content

Finish priority residuals: provider meta, pipeline, macros, ONNX Path B, BYOK - #20

Merged
Ocean82 merged 7 commits into
mainfrom
ocean/priority-residuals-finish-5611
Aug 9, 2026
Merged

Finish priority residuals: provider meta, pipeline, macros, ONNX Path B, BYOK#20
Ocean82 merged 7 commits into
mainfrom
ocean/priority-residuals-finish-5611

Conversation

@Ocean82

@Ocean82 Ocean82 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the Priority Residuals plan (finish-for-real posture):

  1. Provider meta — client parses/stores server meta; BYOK labeled by actual provider/host; DEV chat details
  2. Pipeline cutover — replace brainDispatcher with DeterministicDispatcher + LLMGateway
  3. Real macros — store UndoManager + executeTool; no stub success
  4. ONNX Path Bonnxruntime-node, mount infer route, bundled MiniLM (copy script); keep user upload
  5. Shared BYOK + usage tests — shared SSRF schema; fail-closed usage record memory bump

Spreadsheet-RL-4B is already on prod — no re-upload; local copy script only for dev.

Test plan

  • npx vitest run (client)
  • npm test --prefix server
  • Chat stream shows provider details in DEV
  • Deterministic skills + LLM path without brainDispatcher
  • Macro multi-step mutates sheet and undoes as a group
  • /api/onnx/infer with MiniLM after copy script
  • BYOK private URLs rejected on chat + ai-function
Open in Web Open in Cursor 

Summary by Sourcery

Replace the legacy BrainDispatcher with a split deterministic/LLM pipeline, add real macro execution with grouped undo, expose provider metadata in chat, and introduce a server-side ONNX inference path with hardened BYOK and usage metering.

New Features:

  • Add DeterministicDispatcher and LLMGateway stages to the chat pipeline and wire them into chatService and tests.
  • Implement macro planning/execution pipeline, including execute_macro actions, step execution, and store-backed undo grouping.
  • Expose LLM provider/model metadata from the server to the client and render it in development chat messages.
  • Add a server-side ONNX inference endpoint backed by an onnxruntime session pool and MiniLM model management.

Enhancements:

  • Refine deterministic dispatcher to resolve analysis targets from pipeline context, improving production chat behavior.
  • Update usage recording to fall back to in-memory limits when database writes fail, avoiding silent metering loss.

Build:

  • Add scripts to copy/deploy MiniLM ONNX and optional Spreadsheet-RL-4B GGUF models and document the expected layout.
  • Include onnxruntime-node as a server dependency and wire model path helpers into the ONNX session pool.

Documentation:

  • Rewrite model and environment documentation to cover Spreadsheet-RL-4B as the primary Ollama model and MiniLM ONNX Path B, including operational notes.
  • Add server models README describing ONNX model placement and gitignore behavior.

Tests:

  • Extend pipeline and smoke integration tests for the new deterministic/LLM stages and macro planner behavior.
  • Add unit tests for macro execution, store undo manager, tool step executor, agent client SSE parsing, shared BYOK schema, ONNX model path resolution, and usage metering fallbacks.

Chores:

  • Refactor shared BYOK validation into a single SSRF-hardened schema reused across chat and AI-function endpoints.

Parse server meta through SSE/complete responses into ChatMessage,
show provider details in DEV chat UI, and label BYOK by actual
provider/host instead of hardcoding openrouter.

Co-authored-by: Ocean82 <Ocean82@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@Ocean82, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e32f97c-549c-406c-8c8d-ea9e41e6de5a

📥 Commits

Reviewing files that changed from the base of the PR and between 7e27abb and 2c7ffea.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • server/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (47)
  • .gitignore
  • docs/ENV.md
  • models/README.md
  • package.json
  • scripts/copy-deploy-models.mjs
  • server/models/.gitkeep
  • server/models/README.md
  • server/package.json
  • server/src/index.ts
  • server/src/onnx/modelPaths.test.ts
  • server/src/onnx/modelPaths.ts
  • server/src/onnx/onnxruntime-node.d.ts
  • server/src/onnx/sessionPool.ts
  • server/src/schemas/aiFunction.ts
  • server/src/schemas/byok.test.ts
  • server/src/schemas/byok.ts
  • server/src/schemas/chat.ts
  • server/src/schemas/index.ts
  • server/src/usage.test.ts
  • server/src/usage.ts
  • src/ai/agentClient.test.ts
  • src/ai/agentClient.ts
  • src/ai/brain.ts
  • src/ai/macro/__tests__/macroExecutor.test.ts
  • src/ai/macro/__tests__/storeUndoManager.test.ts
  • src/ai/macro/__tests__/toolStepExecutor.test.ts
  • src/ai/macro/index.ts
  • src/ai/macro/macroExecutor.ts
  • src/ai/macro/macroPlanManager.ts
  • src/ai/macro/storeUndoManager.ts
  • src/ai/macro/toolStepExecutor.ts
  • src/ai/pipeline/__tests__/macroPlanner.test.ts
  • src/ai/pipeline/__tests__/pipeline.integration.test.ts
  • src/ai/pipeline/__tests__/smokeTest.integration.test.ts
  • src/ai/pipeline/index.ts
  • src/ai/pipeline/stages/brainDispatcher.ts
  • src/ai/pipeline/stages/deterministicDispatcher.ts
  • src/ai/pipeline/stages/index.ts
  • src/ai/pipeline/stages/llmGateway.ts
  • src/ai/pipeline/stages/macroPlanner.ts
  • src/ai/responseBuilder.ts
  • src/ai/types.ts
  • src/components/ChatPanel.tsx
  • src/onnx/inputValidator.property.test.ts
  • src/services/chatService.ts
  • src/store/useStore.ts
  • src/types/api.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Reviewer's Guide

Cut over the chat pipeline from BrainDispatcher to separate DeterministicDispatcher + LLMGateway, add a MacroPlanner + real macro execution with undo grouping, wire server-side ONNX Path B for MiniLM, and propagate server LLM provider metadata (including BYOK) end-to-end into dev chat UI while hardening BYOK SSRF validation and usage metering.

Sequence diagram for execute_macro macro execution with undo grouping

sequenceDiagram
  actor User
  participant ChatService as processChatMessage
  participant Store as executeAction
  participant MacroAction as executeMacroAction
  participant UndoMgr as createStoreUndoManager
  participant StepExec as createToolStepExecutor
  participant ExecMacro as executeMacro

  User ->> ChatService: input
  ChatService ->> Store: AgentAction (tool=execute_macro)
  Store ->> MacroAction: executeMacroAction
  MacroAction ->> UndoMgr: createStoreUndoManager
  MacroAction ->> StepExec: createToolStepExecutor
  MacroAction ->> ExecMacro: executeMacro(plan, callbacks, undoManager, stepExecutor)
  ExecMacro -->> MacroAction: MacroExecutionResult
  MacroAction ->> Store: set undoStack entry
  MacroAction -->> Store: ExecutionResult (message, modified)
  Store -->> ChatService: ExecutionResult
Loading

Flow diagram for updated chat pipeline with MacroPlanner, DeterministicDispatcher, and LLMGateway

flowchart LR
  U[User message]
  CS[processChatMessage]
  PR[PipelineRouter]
  AP[createAgentParserStage]
  TR[createTemplateResolverStage]
  IC[createIntentClassifierStage]
  MP[createMacroPlannerStage]
  DD[createDeterministicDispatcherStage]
  LG[createLLMGatewayStage]
  CM[ChatMessage]

  U --> CS --> PR
  PR --> AP --> TR --> IC --> MP --> DD --> LG

  LG -->|ToolResult with providerMeta| CM
  DD -->|ToolResult with providerMeta| CM
  MP -->|execute_macro actions| CM
Loading

File-Level Changes

Change Details Files
Replace BrainDispatcher with DeterministicDispatcher and LLMGateway in the chat pipeline and tests.
  • Update pipeline integration and smoke tests to mock agentClient, intent/mode classifiers, and deterministic analysis modules instead of brain.processMessage.
  • Wire createDeterministicDispatcherStage and createLLMGatewayStage into pipeline router and chatService, including new defaultIntent helper.
  • Modify stageResultToChatMessage to treat deterministic-dispatcher and llm-gateway like brain-dispatcher for ToolResult rendering and local fallback behavior.
src/ai/pipeline/__tests__/pipeline.integration.test.ts
src/ai/pipeline/__tests__/smokeTest.integration.test.ts
src/ai/pipeline/stages/deterministicDispatcher.ts
src/services/chatService.ts
src/ai/pipeline/index.ts
src/ai/pipeline/stages/index.ts
src/ai/pipeline/stages/llmGateway.ts
src/ai/brain.ts
Introduce real macro execution with toolStepExecutor, storeUndoManager, and pipeline MacroPlanner stage so multi-step macros are grouped under a single undo entry.
  • Add execute_macro handling to useStore executeAction, including executeMacroAction that normalizes steps, runs executeMacro with createStoreUndoManager + createToolStepExecutor, and pushes a grouped undo patch on success.
  • Ensure pushHistory is skipped for execute_macro so the macro undo manager owns the transaction.
  • Implement MacroPlanner pipeline stage that segments multi-clause inputs into clauses, parses each via AgentParser, and emits a single pending execute_macro action when all clauses are understood.
  • Replace macroExecutor’s defaultStepExecutor semantics by requiring a StepExecutor argument and add createToolStepExecutor + normalizeStepParams to call executeToolAsync with resolved tool names.
  • Provide createStoreUndoManager for snapshot-based workbook undo and update macroPlanManager/createMacroPlanManager to take an explicit StepExecutor.
  • Add unit tests for macroExecutor with defaultStepExecutor, macroPlanner, toolStepExecutor, and storeUndoManager.
src/store/useStore.ts
src/ai/macro/macroExecutor.ts
src/ai/macro/macroPlanManager.ts
src/ai/macro/storeUndoManager.ts
src/ai/macro/toolStepExecutor.ts
src/ai/macro/index.ts
src/ai/pipeline/stages/macroPlanner.ts
src/ai/pipeline/__tests__/macroPlanner.test.ts
src/ai/macro/__tests__/macroExecutor.test.ts
src/ai/macro/__tests__/toolStepExecutor.test.ts
src/ai/macro/__tests__/storeUndoManager.test.ts
Add ONNX Path B for MiniLM on the server with model path helpers, session pool wiring, and deployment copy script, plus docs and tests.
  • Wire SessionPool for ONNX models into server index, configure frequentlyUsedModels and model path/size resolvers, mount /api/onnx routes behind auth, start reaper and warmup on server start, and dispose on SIGTERM/SIGINT.
  • Implement resolveOnnxModelPath/getOnnxModelSize utilities preferring nested server/models/{name}/model.onnx over flat server/models/{name}.onnx and add unit tests.
  • Add server/models/README.md and update models/README.md and docs/ENV.md with MiniLM deployment instructions, Path B semantics, and Spreadsheet-RL-4B prod notes.
  • Create scripts/copy-deploy-models.mjs to copy or download MiniLM ONNX/tokenizer files into server/models/minilm and optionally public/models/minilm, with optional Spreadsheet-RL-4B GGUF copy for local dev.
  • Add onnxruntime-node dependency and tweak onnx inputValidator property test to assert row-major ordering more robustly.
server/src/index.ts
server/src/onnx/modelPaths.ts
server/src/onnx/modelPaths.test.ts
server/src/onnx/sessionPool.ts
server/models/README.md
scripts/copy-deploy-models.mjs
models/README.md
docs/ENV.md
server/package.json
src/onnx/inputValidator.property.test.ts
Propagate server LLM provider metadata (including BYOK provider/model) through SSE, pipeline, and chat UI.
  • Extend ServerChatResponse and ChatMessage with ProviderMeta and add parseCompleteSseEvent to extract type=complete SSE events (including meta) while continuing to stream token events.
  • Update chatWithAgentServerStream to use parseCompleteSseEvent and serverResponseToChatMessage to copy meta into ChatMessage.providerMeta.
  • Modify llmGateway stage to include providerMeta from serverResult.metadata and responseBuilder.toolResultToChatMessage to pass ToolResult.providerMeta into ChatMessage.
  • In server runLlmChat, compute providerMeta for BYOK (derived from provider/baseUrl/model) and for built-in providers via getModelName, avoid retrying against BYOK credentials, and use providerMeta consistently in both initial and retry ToolResult responses.
  • Render provider details in ChatPanel for assistant messages in dev builds using msg.providerMeta, and add unit tests for SSE meta parsing and ChatMessage mapping.
src/ai/agentClient.ts
src/ai/agentClient.test.ts
src/types/api.ts
src/ai/pipeline/stages/llmGateway.ts
src/ai/responseBuilder.ts
src/ai/types.ts
src/components/ChatPanel.tsx
server/src/index.ts
src/ai/brain.ts
Unify and harden BYOK SSRF validation via shared schemas and extend usage metering to fail closed with an in-memory limiter fallback.
  • Extract BYOK validation into server/src/schemas/byok.ts with isPublicHttpsByokUrl, byokBaseUrlSchema, and byokSchema; reuse it in chat and aiFunction schemas and re-export from schemas index.
  • Add tests verifying isPublicHttpsByokUrl behavior and that SSRF-ish BYOK baseUrls are rejected consistently for both chatBodySchema and aiFunctionBodySchema while valid public BYOK configs are accepted.
  • Update runLlmChat BYOK labeling to derive provider/model from provider field or baseUrl hostname instead of using a generic label and refine retry logic to skip BYOK on the correction retry.
  • Change recordUsage to bump memoryUsage counters when DB writes fail so metering continues to enforce limits even under database outages, and add tests covering DB failure behavior for checkUsage and recordUsage.
  • Slightly adjust docs/ENV.md and production checklist with BYOK/ONNX details and secret rotation notes.
server/src/schemas/byok.ts
server/src/schemas/byok.test.ts
server/src/schemas/chat.ts
server/src/schemas/aiFunction.ts
server/src/schemas/index.ts
server/src/index.ts
server/src/usage.ts
server/src/usage.test.ts
docs/ENV.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

cursoragent and others added 6 commits August 9, 2026 16:12
Replace brainDispatcher/processMessage mocks with deterministic-dispatcher
and llm-gateway stages, mocking agentClient, intent/mode, and analysis deps.

Co-authored-by: Ocean82 <Ocean82@users.noreply.github.com>
Replace brainDispatcher terminal stage with split stages that consume
IntentClassifier enrichment. Remove deprecated brainDispatcher module and
update integration/smoke tests.

Co-authored-by: Ocean82 <Ocean82@users.noreply.github.com>
Require an explicit stepExecutor (no silent stub in production), add
store-backed UndoManager + toolStepExecutor, insert MacroPlanner between
IntentClassifier and DeterministicDispatcher, and run execute_macro from
Apply as one undoable workbook transaction.

Co-authored-by: Ocean82 <Ocean82@users.noreply.github.com>
Wire authenticated /api/onnx with SessionPool path resolution under
server/models, add model:copy-deploy script with HF download fallback,
and document prod Spreadsheet-RL verify-only plus MiniLM setup.

Co-authored-by: Ocean82 <Ocean82@users.noreply.github.com>
Extract shared byokSchema for chat + ai-function, add SSRF regression
tests, bump in-memory counters when usage DB writes fail, and document
Spreadsheet-RL prod verify + credential rotation in ENV checklist.

Co-authored-by: Ocean82 <Ocean82@users.noreply.github.com>
Incomplete intent fixtures broke tsc, and the property test assumed at least two tensor elements which fails for 1x1 shapes.
@Ocean82
Ocean82 marked this pull request as ready for review August 9, 2026 17:36
Copilot AI lite review requested due to automatic review settings August 9, 2026 17:36

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 security issue, 4 other issues, and left some high level feedback:

Security issues:

  • adm-zip: Denial of Service via crafted ZIP file leading to excessive memory allocation (link)

General comments:

  • The new pipeline no longer uses brain-dispatcher, but stageResultToChatMessage still branches on that stage name; consider simplifying this logic to only reference the active stages (macro-planner, deterministic-dispatcher, llm-gateway) to avoid dead paths and keep behavior aligned.
  • createMacroPlannerStage accepts a MacroPlannerDeps with buildExecContext that is never used; either remove this dependency from the interface/exports or hook it into clause parsing so the stage’s contract doesn’t include unused parameters.
  • Both executeMacroAction and createStoreUndoManager perform structuredClone on the workbook, which can be expensive for large documents; consider reusing snapshots or capturing more targeted diffs to reduce cloning overhead during multi-step macro execution and undo operations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new pipeline no longer uses `brain-dispatcher`, but `stageResultToChatMessage` still branches on that stage name; consider simplifying this logic to only reference the active stages (`macro-planner`, `deterministic-dispatcher`, `llm-gateway`) to avoid dead paths and keep behavior aligned.
- `createMacroPlannerStage` accepts a `MacroPlannerDeps` with `buildExecContext` that is never used; either remove this dependency from the interface/exports or hook it into clause parsing so the stage’s contract doesn’t include unused parameters.
- Both `executeMacroAction` and `createStoreUndoManager` perform `structuredClone` on the workbook, which can be expensive for large documents; consider reusing snapshots or capturing more targeted diffs to reduce cloning overhead during multi-step macro execution and undo operations.

## Individual Comments

### Comment 1
<location path="src/ai/agentClient.ts" line_range="25-34" />
<code_context>
+}
+
+/** Parse an SSE `data:` JSON payload into a ServerChatResponse when type=complete. */
+export function parseCompleteSseEvent(jsonStr: string): ServerChatResponse | null {
+  try {
+    const event = JSON.parse(jsonStr) as {
</code_context>
<issue_to_address>
**suggestion:** Avoid double JSON.parse for SSE events to reduce overhead and edge-case differences.

In `chatWithAgentServerStream`, we `JSON.parse(jsonStr)` to check `type === 'token'`, then call `parseCompleteSseEvent(jsonStr)`, which parses the same string again. This adds per-event overhead and splits token vs. complete handling across two parsing paths. Consider changing `parseCompleteSseEvent` to accept a parsed object (or introducing a helper that branches on a single parsed payload) so each event is parsed once and both code paths share the same parsing logic.

Suggested implementation:

```typescript
/** Parse a pre-parsed SSE `data:` JSON payload into a ServerChatResponse when type=complete. */
export function parseCompleteSseEvent(event: {
  type?: string
  message?: string
  actions?: ServerAgentAction[]
  source?: string
  reasoning?: string
  suggestions?: string[]
  meta?: ProviderMeta
}): ServerChatResponse | null {
  if (event.type !== 'complete' || typeof event.message !== 'string') return null
  return {

```

To fully implement the suggestion and avoid double JSON.parse:

1. In `chatWithAgentServerStream`, change the SSE handler so that for each `data:` event you:
   - Call `JSON.parse(jsonStr)` once to obtain `const event = ...`.
   - Branch on `event.type === 'token'` for token handling.
   - Call `parseCompleteSseEvent(event)` for the complete case instead of passing `jsonStr`.

2. Ensure any other call sites of `parseCompleteSseEvent` are updated to pass a parsed event object rather than a string.

3. If you want a shared helper, you can introduce something like:
   ```ts
   function parseSseEventPayload(jsonStr: string): {
     type?: string
     message?: string
     actions?: ServerAgentAction[]
     source?: string
     reasoning?: string
     suggestions?: string[]
     meta?: ProviderMeta
   } | null {
     try {
       return JSON.parse(jsonStr)
     } catch {
       return null
     }
   }
   ```
   and use it in `chatWithAgentServerStream` before calling `parseCompleteSseEvent`.
</issue_to_address>

### Comment 2
<location path="src/ai/macro/toolStepExecutor.ts" line_range="54" />
<code_context>
+    next.column = next.columns[0]
+  }
+
+  if (tool === 'filter') {
+    const operators = Array.isArray(next.operators) ? next.operators : []
+    const values = Array.isArray(next.values) ? next.values : []
</code_context>
<issue_to_address>
**issue (bug_risk):** Filter-specific param normalization depends on the resolved tool name, which might diverge from the NLP name.

Because `tool` is the resolved name, `if (tool === 'filter')` will fail if the NLP intent `filter` is ever mapped to a different canonical tool (e.g. `filter_sheet`), and the `operators`/`values` normalization won’t run for those tools. Consider branching on the original `step.tool` before resolution, or using a broader condition (e.g. `tool.startsWith('filter')`) so filter-style tools continue to receive normalization as registry names change.
</issue_to_address>

### Comment 3
<location path="scripts/copy-deploy-models.mjs" line_range="113-115" />
<code_context>
+async function downloadFile(url, dest) {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Script assumes global fetch in Node; consider guarding or documenting Node version requirements.

This script calls `await fetch(url)` and will throw in Node versions without a global `fetch`. Please either add a runtime check that enforces a minimum Node version with a clear error, or import a small fetch polyfill so the dependency is explicit and the script behaves consistently across environments.

```suggestion
async function downloadFile(url, dest) {
  if (typeof globalThis.fetch !== 'function') {
    throw new Error(
      'scripts/copy-deploy-models.mjs requires a Node.js version with global fetch (Node 18+) or a fetch polyfill'
    )
  }

  console.log(`  downloading ${url}`)
  const res = await globalThis.fetch(url)
```
</issue_to_address>

### Comment 4
<location path="src/ai/macro/__tests__/toolStepExecutor.test.ts" line_range="36" />
<code_context>
new file mode 100644
index 0000000..a1b767c
--- /dev/null
+++ b/src/ai/macro/__tests__/toolStepExecutor.test.ts
</code_context>
<issue_to_address>
**suggestion (testing):** Extend toolStepExecutor tests to cover filter operator/value normalization and unknown operator behavior

These tests cover sort/format mapping and error propagation, but the new `normalizeStepParams` behavior for filter steps isn’t exercised. Please add: (1) a test where a filter step includes `operators`/`values` and assert that the `condition` and `value` passed to `executeToolAsync` are correctly mapped, and (2) a test with an unknown operator string to confirm we fall back to the raw operator instead of throwing, so the NLP-to-tool execution contract is verified end-to-end.
</issue_to_address>

### Comment 5
<location path="server/package-lock.json" line_range="1648-1656" />
<code_context>

</code_context>
<issue_to_address>
**security (CVE-2026-39244):** adm-zip: Denial of Service via crafted ZIP file leading to excessive memory allocation

adm-zip before 0.5.18 is vulnerable to denial of service via a crafted ZIP file with a manipulated uncompressed size header field. In zipEntry.js line 103, Buffer.alloc(_centralHeader.size) allocates memory based on the declared uncompressed size from the ZIP central directory header without validating it against the actual compressed data size or imposing any upper bound. The size value is read directly from the binary header at entryHeader.js line 266 with no bounds check. An attacker can craft a ~120-byte ZIP file that declares ~4GB uncompressed size, causing a memory allocation amplification ratio of over 33 million to 1. The allocation occurs before CRC validation, so the malicious payload cannot be rejected early. All extraction and read methods are affected: readFile(), readAsText(), extractEntryTo(), extractAllTo(), extractAllToAsync(), test(), and entry.getData(). Any application accepting untrusted ZIP files via adm-zip is vulnerable to immediate process crash.

*Source: trivy*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/ai/agentClient.ts
Comment on lines +25 to +34
export function parseCompleteSseEvent(jsonStr: string): ServerChatResponse | null {
try {
const event = JSON.parse(jsonStr) as {
type?: string
message?: string
actions?: ServerAgentAction[]
source?: string
reasoning?: string
suggestions?: string[]
meta?: ProviderMeta

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Avoid double JSON.parse for SSE events to reduce overhead and edge-case differences.

In chatWithAgentServerStream, we JSON.parse(jsonStr) to check type === 'token', then call parseCompleteSseEvent(jsonStr), which parses the same string again. This adds per-event overhead and splits token vs. complete handling across two parsing paths. Consider changing parseCompleteSseEvent to accept a parsed object (or introducing a helper that branches on a single parsed payload) so each event is parsed once and both code paths share the same parsing logic.

Suggested implementation:

/** Parse a pre-parsed SSE `data:` JSON payload into a ServerChatResponse when type=complete. */
export function parseCompleteSseEvent(event: {
  type?: string
  message?: string
  actions?: ServerAgentAction[]
  source?: string
  reasoning?: string
  suggestions?: string[]
  meta?: ProviderMeta
}): ServerChatResponse | null {
  if (event.type !== 'complete' || typeof event.message !== 'string') return null
  return {

To fully implement the suggestion and avoid double JSON.parse:

  1. In chatWithAgentServerStream, change the SSE handler so that for each data: event you:

    • Call JSON.parse(jsonStr) once to obtain const event = ....
    • Branch on event.type === 'token' for token handling.
    • Call parseCompleteSseEvent(event) for the complete case instead of passing jsonStr.
  2. Ensure any other call sites of parseCompleteSseEvent are updated to pass a parsed event object rather than a string.

  3. If you want a shared helper, you can introduce something like:

    function parseSseEventPayload(jsonStr: string): {
      type?: string
      message?: string
      actions?: ServerAgentAction[]
      source?: string
      reasoning?: string
      suggestions?: string[]
      meta?: ProviderMeta
    } | null {
      try {
        return JSON.parse(jsonStr)
      } catch {
        return null
      }
    }

    and use it in chatWithAgentServerStream before calling parseCompleteSseEvent.

Fix in Cursor

next.column = next.columns[0]
}

if (tool === 'filter') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Filter-specific param normalization depends on the resolved tool name, which might diverge from the NLP name.

Because tool is the resolved name, if (tool === 'filter') will fail if the NLP intent filter is ever mapped to a different canonical tool (e.g. filter_sheet), and the operators/values normalization won’t run for those tools. Consider branching on the original step.tool before resolution, or using a broader condition (e.g. tool.startsWith('filter')) so filter-style tools continue to receive normalization as registry names change.

Fix in Cursor

Comment on lines +113 to +115
async function downloadFile(url, dest) {
console.log(` downloading ${url}`)
const res = await fetch(url)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Script assumes global fetch in Node; consider guarding or documenting Node version requirements.

This script calls await fetch(url) and will throw in Node versions without a global fetch. Please either add a runtime check that enforces a minimum Node version with a clear error, or import a small fetch polyfill so the dependency is explicit and the script behaves consistently across environments.

Suggested change
async function downloadFile(url, dest) {
console.log(` downloading ${url}`)
const res = await fetch(url)
async function downloadFile(url, dest) {
if (typeof globalThis.fetch !== 'function') {
throw new Error(
'scripts/copy-deploy-models.mjs requires a Node.js version with global fetch (Node 18+) or a fetch polyfill'
)
}
console.log(` downloading ${url}`)
const res = await globalThis.fetch(url)

Fix in Cursor

})
})

describe('createToolStepExecutor', () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Extend toolStepExecutor tests to cover filter operator/value normalization and unknown operator behavior

These tests cover sort/format mapping and error propagation, but the new normalizeStepParams behavior for filter steps isn’t exercised. Please add: (1) a test where a filter step includes operators/values and assert that the condition and value passed to executeToolAsync are correctly mapped, and (2) a test with an unknown operator string to confirm we fall back to the raw operator instead of throwing, so the NLP-to-tool execution contract is verified end-to-end.

Fix in Cursor

Comment thread server/package-lock.json
Comment on lines +1648 to +1656
"node_modules/adm-zip": {
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
"integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==",
"license": "MIT",
"engines": {
"node": ">=12.0"
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (CVE-2026-39244): adm-zip: Denial of Service via crafted ZIP file leading to excessive memory allocation

adm-zip before 0.5.18 is vulnerable to denial of service via a crafted ZIP file with a manipulated uncompressed size header field. In zipEntry.js line 103, Buffer.alloc(_centralHeader.size) allocates memory based on the declared uncompressed size from the ZIP central directory header without validating it against the actual compressed data size or imposing any upper bound. The size value is read directly from the binary header at entryHeader.js line 266 with no bounds check. An attacker can craft a ~120-byte ZIP file that declares ~4GB uncompressed size, causing a memory allocation amplification ratio of over 33 million to 1. The allocation occurs before CRC validation, so the malicious payload cannot be rejected early. All extraction and read methods are affected: readFile(), readAsText(), extractEntryTo(), extractAllTo(), extractAllToAsync(), test(), and entry.getData(). Any application accepting untrusted ZIP files via adm-zip is vulnerable to immediate process crash.

Source: trivy

Fix in Cursor

@Ocean82
Ocean82 merged commit 766d778 into main Aug 9, 2026
4 of 5 checks passed
@Ocean82
Ocean82 deleted the ocean/priority-residuals-finish-5611 branch August 9, 2026 17:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR finishes several “Priority Residuals” items by (1) cutting over the client-side chat pipeline from the legacy BrainDispatcher to a split deterministic + server-LLM gateway, (2) introducing real macro planning/execution with grouped undo behavior, (3) propagating server-reported LLM provider/model metadata end-to-end for dev visibility, and (4) wiring up server-side ONNX “Path B” inference with a model copy/download script plus SSRF-hardened shared BYOK schemas and usage-metering fail-closed behavior.

Changes:

  • Replace legacy brainDispatcher pipeline terminal stage with MacroPlannerDeterministicDispatcherLLMGateway, and propagate provider metadata into chat messages.
  • Implement production macro execution (execute_macro) with a real step executor and grouped undo snapshots.
  • Add ONNX Path B server route/session-pool wiring and a deploy-model copy/download script; unify BYOK SSRF schemas across endpoints; harden usage metering on DB failures.

Reviewed changes

Copilot reviewed 44 out of 49 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/types/api.ts Adds ProviderMeta and attaches it to ChatMessage for provider/model attribution.
src/store/useStore.ts Adds execute_macro action execution with grouped undo behavior and adjusts history handling.
src/services/chatService.ts Rewires chat pipeline stages (macro planner + deterministic dispatcher + LLM gateway) and maps provider metadata into rendered messages.
src/onnx/inputValidator.property.test.ts Tightens property test assertions to handle small tensor sizes safely.
src/components/ChatPanel.tsx Renders provider/model details in DEV builds for assistant messages when available.
src/ai/types.ts Extends ToolResult to carry provider metadata.
src/ai/responseBuilder.ts Propagates providerMeta from ToolResult into ChatMessage.
src/ai/pipeline/stages/macroPlanner.ts Introduces a macro planning stage that emits a pending execute_macro action for multi-clause commands.
src/ai/pipeline/stages/llmGateway.ts Adds provider metadata from server responses into stage metadata.
src/ai/pipeline/stages/index.ts Exports new pipeline stages (macro planner, deterministic dispatcher) and removes legacy brain dispatcher export.
src/ai/pipeline/stages/deterministicDispatcher.ts Allows resolving analysis target from PipelineContext when not injected (better for production chatService wiring).
src/ai/pipeline/stages/brainDispatcher.ts Removes the legacy transitional BrainDispatcher stage implementation.
src/ai/pipeline/index.ts Updates pipeline exports to reflect the new stage split.
src/ai/pipeline/tests/smokeTest.integration.test.ts Updates integration smoke tests to match the new deterministic + LLM gateway pipeline behavior.
src/ai/pipeline/tests/pipeline.integration.test.ts Updates end-to-end routing tests to reflect new stage order and LLM gateway usage.
src/ai/pipeline/tests/macroPlanner.test.ts Adds unit tests for macro planner stage claim/pass-through behavior.
src/ai/macro/toolStepExecutor.ts Implements a real macro step executor mapping macro steps to executeToolAsync, with param normalization.
src/ai/macro/storeUndoManager.ts Adds a store-backed undo manager that snapshots/restores workbook state for macro transactions.
src/ai/macro/macroPlanManager.ts Requires an injected step executor (no silent stub default) and wires it into macro execution.
src/ai/macro/macroExecutor.ts Makes step executor required for executeMacro and clarifies stub is test-only.
src/ai/macro/index.ts Adds public exports for macro execution/manager helpers and executors.
src/ai/macro/tests/toolStepExecutor.test.ts Adds unit tests for step param normalization and tool mapping to executeToolAsync.
src/ai/macro/tests/storeUndoManager.test.ts Adds unit tests verifying rollback/commit snapshot behavior.
src/ai/macro/tests/macroExecutor.test.ts Updates macro executor tests for required step executor argument.
src/ai/brain.ts Updates legacy orchestrator notes and ensures macro plan manager uses the default test executor for macro unit tests.
src/ai/agentClient.ts Adds parsing for SSE complete events including meta provider info; maps server provider meta onto ChatMessage.
src/ai/agentClient.test.ts Adds unit tests asserting SSE complete event parsing preserves provider metadata.
server/src/usage.ts On DB write failure, bumps in-memory limiter to avoid undercounting during outages (fail-closed-ish behavior).
server/src/usage.test.ts Adds tests for DB failure fallback behavior (check + record paths).
server/src/schemas/index.ts Re-exports shared BYOK schemas/utilities from a central module.
server/src/schemas/chat.ts Switches chat schema to use shared BYOK schema (prevents SSRF validation drift).
server/src/schemas/byok.ts Introduces shared SSRF-hardened BYOK Zod schemas + helper for public HTTPS validation.
server/src/schemas/byok.test.ts Adds tests verifying SSRF BYOK rejection on both chat + ai-function schemas.
server/src/schemas/aiFunction.ts Switches ai-function schema to use shared BYOK schema (prevents SSRF validation drift).
server/src/onnx/sessionPool.ts Updates default model path resolution comment and relies on injected resolver for Path B wiring.
server/src/onnx/onnxruntime-node.d.ts Removes the local type stub now that onnxruntime-node is installed.
server/src/onnx/modelPaths.ts Adds filesystem-based model path resolution utilities for nested vs flat layouts.
server/src/onnx/modelPaths.test.ts Adds unit tests for ONNX model path and size resolution utilities.
server/src/index.ts Mounts /api/onnx with session pool, logs ONNX readiness, warms pool, and disposes on shutdown; adds provider meta tracking for LLM responses including BYOK.
server/package.json Adds onnxruntime-node dependency for server-side inference.
server/package-lock.json Updates lockfile for onnxruntime-node and transitive dependencies.
server/models/README.md Documents server-side ONNX model layout and the copy/download workflow.
server/models/.gitkeep Keeps server/models/ tracked while weights remain gitignored.
scripts/copy-deploy-models.mjs Adds a cross-platform model copy/download script for MiniLM (plus optional GGUF copy).
package.json Adds model:copy-deploy script to run the new deploy model copy/download helper.
package-lock.json Updates root lockfile metadata (and any associated dependency graph updates).
models/README.md Updates model docs to reflect prod Spreadsheet-RL-4B posture and MiniLM Path B copy/deploy workflow.
docs/ENV.md Documents SMARTSHT_MINILM_SRC and ONNX Path B operational notes.
.gitignore Gitignores server ONNX weights while keeping README + .gitkeep tracked.
Files not reviewed (1)
  • server/package-lock.json: Generated file
Suppressed comments (1)

src/ai/agentClient.ts:142

  • chatWithAgentServerStream parses jsonStr and then calls parseCompleteSseEvent(jsonStr), which re-parses the same JSON. After updating parseCompleteSseEvent to accept an already-parsed object, pass parsed to avoid the extra JSON.parse on every non-token SSE line.
          }
          const complete = parseCompleteSseEvent(jsonStr)
          if (complete) finalResponse = complete

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 409 to 413
/**
* Resolve a model name to a file path.
* Default prefers nested models/{name}/model.onnx, then flat models/{name}.onnx.
* Prefer injecting resolveModelPath at mount time (see index.ts + modelPaths.ts).
*/
Comment thread server/src/index.ts
Comment on lines +122 to +123
// Models live under server/models/ when the process cwd is server/.
const modelsRoot = path.resolve(process.cwd(), 'models')
Comment thread src/ai/agentClient.ts
Comment on lines +25 to 48
export function parseCompleteSseEvent(jsonStr: string): ServerChatResponse | null {
try {
const event = JSON.parse(jsonStr) as {
type?: string
message?: string
actions?: ServerAgentAction[]
source?: string
reasoning?: string
suggestions?: string[]
meta?: ProviderMeta
}
if (event.type !== 'complete' || typeof event.message !== 'string') return null
return {
message: event.message,
actions: Array.isArray(event.actions) ? event.actions : [],
source: (event.source as ServerChatResponse['source']) ?? 'llm',
reasoning: event.reasoning,
suggestions: event.suggestions,
meta: event.meta,
}
} catch {
return null
}
}
@@ -407,7 +407,7 @@ describe('executeMacro', () => {
describe('injectable step executor', () => {
it('uses default executor when none provided', async () => {
Comment on lines +20 to +22
export interface MacroPlannerDeps {
buildExecContext: () => ExecutionContext
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants