Skip to content

Add HTTP Request and JS Script nodes with Cumulative State Inspector - #4

Open
Jacobcdsmith wants to merge 1 commit into
mainfrom
feature/http-script-nodes-12137423088350218810
Open

Add HTTP Request and JS Script nodes with Cumulative State Inspector#4
Jacobcdsmith wants to merge 1 commit into
mainfrom
feature/http-script-nodes-12137423088350218810

Conversation

@Jacobcdsmith

@Jacobcdsmith Jacobcdsmith commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Added HTTP Request nodes, JS Script nodes, and collapsible cumulative state snapshots to the execution run log drawer, complete with full code generation engines and unit tests.


PR created automatically by Jules for task 12137423088350218810 started by @Jacobcdsmith

Summary by CodeRabbit

  • New Features
    • Added HTTP Request and JavaScript Script node types.
    • HTTP nodes support configurable methods, URLs, headers, bodies, JSON responses, and error handling.
    • Script nodes can modify workflow state and influence routing.
    • Added support for generating executable HTTP and script steps in JavaScript and Python.
    • Added distinct visual styling for the new node types.
    • Run logs can now display cumulative state snapshots for each step.

…ector

This commit introduces two powerful, production-grade node types to the AI agent canvas:
1. HTTP Request node: Executes real client-side REST APIs with custom method, URL, headers, and body support (including state interpolation).
2. JS Script node: Evaluates custom sandboxed JS code to dynamically read/write execution state variables and define conditional branch routing.

Additionally, the Execution Run Log is enhanced with a collapsible cumulative state snapshot panel for each run step, and full codegen is written for both nodes in Python (using urllib.request) and JavaScript (using fetch). Includes high-coverage unit tests for execution and codegen.
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-flow-canvas Ready Ready Preview, Comment Jul 13, 2026 3:01pm

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds HTTP and JavaScript script workflow nodes, including type metadata, runtime execution, Python and JavaScript code generation, node colors, state snapshots in run logs, UI display, and automated tests.

Changes

HTTP and Script Nodes

Layer / File(s) Summary
Node contracts and presentation
frontend/src/flow/types.ts, frontend/src/index.css, frontend/src/flow/AgentNode.tsx, frontend/src/flow/Palette.tsx
Defines HTTP and script node metadata, adds their color variables, and maps both kinds to themed node colors.
Runtime execution and state logging
frontend/src/flow/runFlow.ts, frontend/src/pages/Index.tsx, frontend/src/test/newNodes.test.ts
Executes HTTP requests and scripts, supports string-based routing, records deep-cloned state snapshots, displays snapshots in log items, and tests success, failure, routing, and state mutation.
Python and JavaScript code generation
frontend/src/flow/codegen.ts, frontend/src/test/newNodes.test.ts
Generates HTTP adapters and HTTP/script node bodies for Python and JavaScript, includes the new kinds in ALL_KINDS, and verifies generated output.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FlowRunner
  participant HTTPEndpoint
  participant ScriptNode
  participant RunLog
  FlowRunner->>HTTPEndpoint: Send interpolated HTTP request
  HTTPEndpoint-->>FlowRunner: Return JSON or text response
  FlowRunner->>RunLog: Record output and state snapshot
  FlowRunner->>ScriptNode: Execute script with workflow state
  ScriptNode-->>FlowRunner: Mutate state and return routing label
  FlowRunner->>RunLog: Record routed execution and state snapshot
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main additions: HTTP Request and JS Script nodes, plus the cumulative state inspector in the run log.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/http-script-nodes-12137423088350218810

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
frontend/src/test/newNodes.test.ts (1)

42-114: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert on the actual fetch call args to cover state interpolation.

This test only checks the parsed response/output, not that url, headers, and body were correctly interpolated from state before being sent (e.g. {{state.query}}" in the body). Since state interpolation is a headline feature of the http node, asserting global.fetch was called with the expected interpolated url/options would meaningfully strengthen coverage of the new logic (and would have caught the raw-string-into-JSON interpolation risk flagged in runFlow.ts).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/test/newNodes.test.ts` around lines 42 - 114, Strengthen the
test around runFlow by asserting the mocked global.fetch invocation for node n2,
including the expected URL and request options with state.query interpolated
into the JSON body. Preserve the existing response, output, and stateSnapshot
assertions while verifying headers and body are sent in their parsed/serialized
format rather than retaining the raw template.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/flow/codegen.ts`:
- Around line 215-219: Update the generated Python helper in
frontend/src/flow/codegen.ts lines 215-219 to raise RuntimeError with the
invalid-JSON exception details instead of silently passing, and update the
generated JavaScript helper at frontend/src/flow/codegen.ts line 397 to catch
JSON.parse failures and throw the descriptive HTTP headers error. Ensure both
helpers fail fast consistently with runtime behavior.
- Line 234: Update generated helpers in frontend/src/flow/codegen.ts at lines
234 and 410: make Python make_http_request collect response headers into
resp_headers and return them with status and body, and make JavaScript
makeHttpRequest build respHeaders from res.headers and include it in the
returned object. Ensure both generated response shapes match runFlow.ts with
status, headers, and body.

In `@frontend/src/flow/runFlow.ts`:
- Around line 317-326: Update the fetch flow in runFlow to use an
AbortController with a finite timeout, aborting requests that exceed the
configured limit and handling the resulting cancellation so the run terminates
cleanly. Ensure the controller and timer are properly cleaned up after fetch
completion or failure, and propagate cancellation rather than leaving running
state stuck.
- Around line 345-366: Update the script execution block in the “script” case to
remove the `with (state)` wrapper and execute userCode directly with the
existing `state` parameter. Require scripts to use explicit `state.foo` reads
and writes, while preserving returned-label handling and the existing Script
evaluation error behavior.

---

Nitpick comments:
In `@frontend/src/test/newNodes.test.ts`:
- Around line 42-114: Strengthen the test around runFlow by asserting the mocked
global.fetch invocation for node n2, including the expected URL and request
options with state.query interpolated into the JSON body. Preserve the existing
response, output, and stateSnapshot assertions while verifying headers and body
are sent in their parsed/serialized format rather than retaining the raw
template.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e25393b8-6f99-440a-aa52-32e1a9c89989

📥 Commits

Reviewing files that changed from the base of the PR and between 9c0d007 and 4eee47a.

📒 Files selected for processing (8)
  • frontend/src/flow/AgentNode.tsx
  • frontend/src/flow/Palette.tsx
  • frontend/src/flow/codegen.ts
  • frontend/src/flow/runFlow.ts
  • frontend/src/flow/types.ts
  • frontend/src/index.css
  • frontend/src/pages/Index.tsx
  • frontend/src/test/newNodes.test.ts

Comment on lines +215 to +219
lines.push(` if headers_json:`);
lines.push(` try:`);
lines.push(` headers = json.loads(headers_json)`);
lines.push(` except Exception:`);
lines.push(` pass`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Inconsistent header-JSON error handling across generated helpers. The Python helper silently ignores invalid header JSON (except Exception: pass) while the JS helper throws a raw SyntaxError from JSON.parse; the runtime throws a descriptive "HTTP headers must be valid JSON" error. Both generated helpers should match the runtime's fail-fast behavior.

  • frontend/src/flow/codegen.ts#L215-L219: Replace pass with raise RuntimeError(f"HTTP headers must be valid JSON: {exc}").
  • frontend/src/flow/codegen.ts#L397: Wrap JSON.parse(headersJson) in try/catch and throw new Error("HTTP headers must be valid JSON") on failure.
📍 Affects 1 file
  • frontend/src/flow/codegen.ts#L215-L219 (this comment)
  • frontend/src/flow/codegen.ts#L397-L397
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/flow/codegen.ts` around lines 215 - 219, Update the generated
Python helper in frontend/src/flow/codegen.ts lines 215-219 to raise
RuntimeError with the invalid-JSON exception details instead of silently
passing, and update the generated JavaScript helper at
frontend/src/flow/codegen.ts line 397 to catch JSON.parse failures and throw the
descriptive HTTP headers error. Ensure both helpers fail fast consistently with
runtime behavior.

lines.push(` resp_body = json.loads(resp_body)`);
lines.push(` except Exception:`);
lines.push(` pass`);
lines.push(` return {"status": status, "body": resp_body}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Generated HTTP helpers omit response headers (Python and JS). Both make_http_request (Python) and makeHttpRequest (JS) return {status, body} while the runtime in runFlow.ts returns {status, headers, body}, causing a state-shape contract mismatch between the visual runner and generated code.

  • frontend/src/flow/codegen.ts#L234: Add resp_headers = dict(response.headers) and include "headers": resp_headers in the return dict.
  • frontend/src/flow/codegen.ts#L410: Add const respHeaders = Object.fromEntries(res.headers.entries()) and include headers: respHeaders in the return object.
📍 Affects 1 file
  • frontend/src/flow/codegen.ts#L234-L234 (this comment)
  • frontend/src/flow/codegen.ts#L410-L410
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/flow/codegen.ts` at line 234, Update generated helpers in
frontend/src/flow/codegen.ts at lines 234 and 410: make Python make_http_request
collect response headers into resp_headers and return them with status and body,
and make JavaScript makeHttpRequest build respHeaders from res.headers and
include it in the returned object. Ensure both generated response shapes match
runFlow.ts with status, headers, and body.

Comment on lines +317 to +326
const fetchOptions: RequestInit = {
method,
headers: parsedHeaders,
};

if (method !== "GET" && method !== "HEAD" && rawBody) {
fetchOptions.body = rawBody;
}

const res = await fetch(urlStr, fetchOptions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

fetch has no timeout — a hanging request stalls the entire run indefinitely.

There's no AbortController/timeout around this fetch. A slow or non-responding endpoint blocks the whole flow forever. Downstream in Index.tsx, there is also no way to cancel an in-flight run (the drawer's "close" button only hides the panel, it doesn't abort runFlow), so running stays stuck and the flow keeps executing in the background with no user-visible recovery path.

🛡️ Proposed fix
+      const controller = new AbortController();
+      const timeoutId = setTimeout(() => controller.abort(), 15000);
+      let res: Response;
+      try {
+        res = await fetch(urlStr, { ...fetchOptions, signal: controller.signal });
+      } finally {
+        clearTimeout(timeoutId);
+      }
-      const res = await fetch(urlStr, fetchOptions);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fetchOptions: RequestInit = {
method,
headers: parsedHeaders,
};
if (method !== "GET" && method !== "HEAD" && rawBody) {
fetchOptions.body = rawBody;
}
const res = await fetch(urlStr, fetchOptions);
const fetchOptions: RequestInit = {
method,
headers: parsedHeaders,
};
if (method !== "GET" && method !== "HEAD" && rawBody) {
fetchOptions.body = rawBody;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000);
let res: Response;
try {
res = await fetch(urlStr, { ...fetchOptions, signal: controller.signal });
} finally {
clearTimeout(timeoutId);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/flow/runFlow.ts` around lines 317 - 326, Update the fetch flow
in runFlow to use an AbortController with a finite timeout, aborting requests
that exceed the configured limit and handling the resulting cancellation so the
run terminates cleanly. Ensure the controller and timer are properly cleaned up
after fetch completion or failure, and propagate cancellation rather than
leaving running state stuck.

Comment on lines +345 to +366
case "script": {
const userCode = cfg.code || "";
// Create a sandboxed execution where the user can read/write properties on `state`
// We will allow returning a value which gets mapped as custom edge label.
try {
const fn = new Function("state", `
with (state) {
${userCode}
}
`);
// We evaluate using a Proxy or just normal state object. To make standard assignments work,
// we can let standard state mutation happen directly on the state reference.
const returnedLabel = fn(state);
if (typeof returnedLabel === "string") {
state.__router_branch = returnedLabel; // Re-use the branch selector matching logic
return { returned: returnedLabel, success: true };
}
return { success: true };
} catch (e) {
throw new Error(`Script evaluation error: ${e instanceof Error ? e.message : String(e)}`);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file range and nearby helpers.
FILE="frontend/src/flow/runFlow.ts"
wc -l "$FILE"
sed -n '300,410p' "$FILE"

# Search for related script execution / state handling.
rg -n "new Function|with \\(state\\)|__router_branch|script" frontend/src/flow/runFlow.ts frontend/src/flow -S

# Probe JavaScript semantics relevant to the comment.
node - <<'JS'
function run(userCode, state) {
  const fn = new Function("state", `
    with (state) {
      ${userCode}
    }
  `);
  return fn(state);
}

const state = {};
try {
  run("newVar = 5; return typeof newVar + ':' + state.newVar;", state);
  console.log("state after assignment:", JSON.stringify(state));
  console.log("global newVar:", globalThis.newVar);
  delete globalThis.newVar;
} catch (e) {
  console.error("error:", e && e.message);
}

const state2 = {};
try {
  const out = run("state.x = 1; return x + ':' + state.x;", state2);
  console.log("output for state.x assignment:", out);
} catch (e) {
  console.error("error2:", e && e.message);
}

const state3 = { existing: 0 };
try {
  const out = run("existing = 7; return existing + ':' + state.existing;", state3);
  console.log("output for existing property assignment:", out);
  console.log("state3 after:", JSON.stringify(state3));
} catch (e) {
  console.error("error3:", e && e.message);
}
JS

Repository: Jacobcdsmith/agent-flow-canvas

Length of output: 2059


with (state) leaks new assignments out of state. Bare writes like newVar = 5 create globals in this sloppy new Function body instead of updating state, so scripts can appear to work while downstream nodes never see the value. Since state is already passed in, drop the with wrapper and require explicit state.foo writes.

🧰 Tools
🪛 OpenGrep (1.25.0)

[ERROR] 350-354: new Function() with dynamic input can execute arbitrary code. Avoid dynamic code evaluation entirely, or use a safe alternative.

(coderabbit.code-injection.new-function-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/flow/runFlow.ts` around lines 345 - 366, Update the script
execution block in the “script” case to remove the `with (state)` wrapper and
execute userCode directly with the existing `state` parameter. Require scripts
to use explicit `state.foo` reads and writes, while preserving returned-label
handling and the existing Script evaluation error behavior.

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.

1 participant