Skip to content

feat(core): add parallel input guardrails#1347

Merged
omeraplak merged 6 commits into
mainfrom
feature/speculative-input-guardrails
Jun 23, 2026
Merged

feat(core): add parallel input guardrails#1347
omeraplak merged 6 commits into
mainfrom
feature/speculative-input-guardrails

Conversation

@omeraplak

@omeraplak omeraplak commented Jun 23, 2026

Copy link
Copy Markdown
Member

PR Checklist

Please check if your PR fulfills the following requirements:

Bugs / Features

What is the current behavior?

Input guardrails are fully blocking before streamText starts. Slow async checks, including LLM-based policy checks, delay the first stream output. There is no stream-level support for starting those checks in parallel, stopping the stream if they block, overriding visible output, and keeping speculative assistant output out of conversation memory.

What is the new behavior?

Adds parallel input guardrails for streamText through execution: "parallel" and streamPolicy: "holdUntilPass".

Parallel input guardrails now start while the model stream starts, buffer full/text/UI stream output until the guardrails pass, and replace blocked streams with the guardrail message. Blocked runs restore the conversation buffer checkpoint, skip assistant memory persistence, and stop before final hooks/evals persist generated output.

Blocked parallel input guardrails emit an AI SDK-compatible structured frontend event before the replacement text:

  • UI streams emit data-input-guardrail-blocked with { code, reason, message, guardrailId?, guardrailName?, severity? }, so useChat consumers can show localized UI without matching the fallback message string.
  • The docs show the type-safe AI SDK pattern with UIMessage<unknown, { "input-guardrail-blocked": InputGuardrailBlockedEventData }>.
  • Blocked UI streams finish with finishReason: "error".
  • Custom fullStream consumers receive an input-guardrail-blocked part with the same payload.

Parallel input guardrails can allow or block only. If a parallel guardrail returns action: "modify", VoltAgent raises a configuration error; input modification remains supported by the default blocking mode.

The speculative input guardrail runtime now lives in agent/streaming/input-guardrail-stream.ts, keeping agent.ts focused on orchestration.

Tool execution waits for pending parallel input guardrails before running side-effectful server-side tool handlers.

fixes (issue)

N/A

Notes for reviewers

Validation run:

  • pnpm --filter @voltagent/core typecheck
  • pnpm --filter @voltagent/core test:single src/agent/agent.spec.ts src/agent/streaming/guardrail-stream.spec.ts src/agent/streaming/output-guardrail-stream-runner.spec.ts src/agent/memory-persist-queue.spec.ts src/agent/memory-persistence.integration.spec.ts src/agent/guardrail.integration.spec.ts src/agent/guardrail.spec.ts src/agent/conversation-buffer.spec.ts (176 tests)
  • pnpm --filter @voltagent/core test:single src/agent/guardrail.integration.spec.ts src/agent/guardrail.spec.ts src/agent/streaming/guardrail-stream.spec.ts (39 tests after the AI SDK compatibility polish)
  • pnpm --filter @voltagent/core lint

lint exits successfully and still reports the existing cognitive complexity warnings in agent.ts, workflow/steps/and-agent.ts, and workspace/filesystem/backends/filesystem.ts.

Summary by CodeRabbit

  • New Features
    • Added parallel input guardrails for streamText, including configurable execution/stream buffering behavior.
    • Buffered streamed output until parallel guardrails resolve; when blocked, replaces streamed content with the guardrail message and prevents assistant output persistence.
    • Added conversation buffer checkpoints (create/restore) to support guarded stream replacement.
  • Bug Fixes
    • Improved guardrail block error details with guardrail metadata; modify inputs now fail when modifications are disallowed.
    • Tool execution is gated to avoid running when blocked.
  • Documentation
    • Expanded guardrails overview and API guidance for execution modes and UI/streaming event behavior (data-input-guardrail-blocked).
  • Tests
    • Extended integration/unit tests for parallel blocking across text/full/UI streams and event forwarding.

@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e47be1e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@voltagent/core Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces parallel input guardrail execution for streamText in @voltagent/core. Input guardrails can now be configured with execution: "parallel" and streamPolicy: "holdUntilPass", causing async checks to run concurrently with model startup while stream output is buffered. Blocked streams replace output with a guardrail message and skip assistant message persistence. A ConversationBuffer checkpoint/restore mechanism is also added to support rollback when a parallel guardrail blocks.

Changes

Parallel Input Guardrails for streamText

Layer / File(s) Summary
Input guardrail type contracts and execution modes
packages/core/src/agent/types.ts, packages/core/src/agent/guardrail.ts
Adds InputGuardrailExecution, InputGuardrailStreamPolicy, and InputGuardrailDefinition types; updates InputGuardrail to union of GuardrailFunction or InputGuardrailDefinition; extends CreateInputGuardrailOptions, NormalizedInputGuardrail, createInputGuardrail, and normalizeInputGuardrailList with execution and streamPolicy fields, defaulting to "blocking" and "holdUntilPass".
ConversationBuffer checkpoint and restore
packages/core/src/agent/conversation-buffer.ts, packages/core/src/agent/conversation-buffer.spec.ts
Exports ConversationBufferCheckpoint interface; adds createCheckpoint() and restoreCheckpoint() public methods and a private rebuildToolPartIndex() helper; unit test verifies checkpoint-restore round-trip preserves message state and pending ids.
Guardrail validation and allowModify enforcement
packages/core/src/agent/guardrail.ts, packages/core/src/agent/guardrail.spec.ts
Extends runInputGuardrails with allowModify option that throws GUARDRAIL_INPUT_MODIFY_UNSUPPORTED when parallel guardrails attempt modification; updates GUARDRAIL_INPUT_BLOCKED error to include guardrail metadata (id, name, severity); adds unit tests for normalization preservation and modify-rejection behavior.
SpeculativeInputGuardrailRun and stream gating
packages/core/src/agent/context-keys.ts, packages/core/src/agent/streaming/input-guardrail-stream.ts
Introduces SPECULATIVE_INPUT_GUARDRAIL_CONTEXT_KEY symbol; implements SpeculativeInputGuardrailRun class that executes guardrails with allowModify:false, stores pass/blocked decision, and on block restores buffer checkpoint and aborts operation context; exports three stream wrapper functions (full/text/UI) and gateIterableUntilSpeculativeInputPass concurrency controller that buffers source chunks while racing against guardrail resolution.
Stream part types for guardrail blocked events
packages/core/src/agent/subagent/types.ts, packages/core/src/agent/types.ts, packages/core/src/utils/streams/types.ts, packages/core/src/index.ts
Adds InputGuardrailBlockedEventData and InputGuardrailBlockedStreamPart interfaces; introduces VoltAgentStreamMetadata type to centralize optional subagent/agent metadata; refactors VoltAgentTextStreamPart to intersection of metadata with union of TextStreamPart and guardrail-blocked part; updates StreamEventType source to VoltAgentTextStreamPart across multiple files; re-exports new types from package index.
Agent.streamText wiring and stream pipeline gating
packages/core/src/agent/agent.ts
Partitions input guardrails into non-parallel (immediate) and parallel (speculative); initializes SpeculativeInputGuardrailRun with speculative stream applicators on full/text/UI getters; short-circuits onFinish and sanitized-text resolution when blocked.
Tool execution gating on speculative guardrail resolution
packages/core/src/agent/agent.ts, packages/core/src/agent/agent.spec.ts
Adds getSpeculativeInputGuardrail and waitForSpeculativeInputGuardrail helpers; gates both async-generator and non-generator tool execution paths on guardrail decision; modifies createStepHandler to skip persistence when speculative run is pending or blocked; includes unit tests validating tool waits for pass and is not invoked on block.
Subagent stream forwarding for guardrail events
packages/core/src/agent/subagent/index.ts, packages/core/src/agent/subagent/index.spec.ts
Updates SubAgentManager default forwarding to include "input-guardrail-blocked" in both UI and full stream forwarding when types are unset; extends mock sub-agent stream in tests with guardrail-blocked event and validates forwarding assertions.
Integration tests, documentation, and changeset
packages/core/src/agent/guardrail.integration.spec.ts, website/docs/guardrails/overview.md, .changeset/parallel-input-guardrails.md
Adds four integration tests covering guardrail pass (stream held then released), block (stream replaced and memory persistence skipped), block-overrides-output-guardrails, and output-guardrail-precedence; updates guardrails docs with Execution Modes, UI Handling for Parallel Input Blocks, updated registration guidance, and InputGuardrailDefinition API reference; adds changeset entry describing feature behavior and data-input-guardrail-blocked UI event handling.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Agent as Agent.streamText
  participant BlockingGuards as Blocking Input Guardrails
  participant ParallelGuards as Parallel Input Guardrails
  participant Model as LLM Model Stream
  participant GateIterator as gateIterableUntilSpeculativeInputPass
  participant Buffer as ConversationBuffer
  participant StepHandler as Step Persistence Handler

  Client->>Agent: streamText(input, guardrails)
  Agent->>BlockingGuards: execute immediately (allowModify: true)
  BlockingGuards-->>Agent: pass or block/modify
  
  alt Blocking Guardrail Blocks
    Agent-->>Client: error thrown
  else Blocking Guardrails Pass
    Agent->>ParallelGuards: start async (allowModify: false)
    Note over ParallelGuards: runs concurrently with model
    Agent->>Model: begin streaming
    Agent->>GateIterator: wrap stream with pending decision
    
    rect rgba(255, 200, 0, 0.5)
      Model-->>GateIterator: emit text chunks (buffered)
    end
    
    alt Parallel Guardrail Passes
      ParallelGuards-->>GateIterator: decision: passed
      GateIterator-->>Client: yield buffered chunks
      Agent->>StepHandler: persist steps (hasPassed=true)
    else Parallel Guardrail Blocks
      ParallelGuards-->>Agent: decision: blocked + message
      Agent->>Buffer: restoreCheckpoint()
      Agent->>GateIterator: replace stream
      GateIterator-->>Client: yield block message only (finish: error)
      Agent->>StepHandler: skip persistence (hasPassed=false)
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • VoltAgent/voltagent#1334: Both PRs modify packages/core/src/agent/agent.ts Agent.streamText's sanitized/returned text handling; this PR routes final sanitized text through speculative input-guardrail blocking, while the retrieved PR refactors the sanitized text promise to avoid unhandled rejections.

Poem

🐇 A stream once blocked by cautious paws,
Now races model and guardrail laws.
Buffered chunks wait for the all-clear hop,
Then flood the client—or quietly stop!
Parallel checks, no waiting in line,
The rabbit says: async is just fine. 🌟

🚥 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
Title check ✅ Passed The title clearly and specifically describes the primary change: adding parallel input guardrails to the core agent system.
Description check ✅ Passed The description follows the template structure with all required sections completed: checklist items checked, current/new behavior explained, tests added, docs updated, changesets included, and detailed validation notes provided.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/speculative-input-guardrails

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.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploying voltagent with  Cloudflare Pages  Cloudflare Pages

Latest commit: e47be1e
Status:⚡️  Build in progress...

View logs

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/agent/agent.ts (1)

7584-7610: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Gate recordStepResults before persisting blocked output.

The new hasPassed() check only guards persistQueue, but recordStepResults above it can persist step records via memoryManager.saveConversationSteps while the speculative guardrail is still pending. If the guardrail later blocks, assistant/tool-step data may already be stored; skip or defer recordStepResults until there is no speculative guardrail or it has passed.

Suggested direction
-      if (conversationPersistence.mode === "step") {
+      const speculativeInputGuardrailPassed =
+        !speculativeInputGuardrail || speculativeInputGuardrail.hasPassed();
+
+      if (conversationPersistence.mode === "step" && speculativeInputGuardrailPassed) {
         await this.recordStepResults(undefined, oc, {
           awaitPersistence: shouldFlushStepPersistence,
         });
       }
@@
         shouldPersistMemory &&
         persistQueue &&
-        (!speculativeInputGuardrail || speculativeInputGuardrail.hasPassed()) &&
+        speculativeInputGuardrailPassed &&
🤖 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 `@packages/core/src/agent/agent.ts` around lines 7584 - 7610, The
recordStepResults call is not gated by the speculative guardrail check, allowing
step records to be persisted via memoryManager.saveConversationSteps even when a
speculative guardrail is still pending. Add the same guardrail condition that
protects persistQueue to also guard the recordStepResults method call within the
conversationPersistence.mode === "step" block, ensuring that the condition
(!speculativeInputGuardrail || speculativeInputGuardrail.hasPassed()) is
satisfied before calling recordStepResults.
🧹 Nitpick comments (2)
packages/core/src/agent/guardrail.integration.spec.ts (1)

211-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the stream is still gated before guardrail release.

Line 212 starts collection, but Line 215 only checks the final text after release; this can pass even if output leaks early. Add a pre-release unresolved assertion so the “holdUntilPass” behavior is actually verified.

♻️ Proposed test hardening
     const streamResult = await agent.streamText("hello");
     const textPromise = collectTextStream(streamResult.textStream);
     await guardrailStarted;
+    const settledBeforeRelease = await Promise.race([
+      textPromise.then(() => true),
+      Promise.resolve(false),
+    ]);
+    expect(settledBeforeRelease).toBe(false);
     releaseGuardrail();
     await expect(textPromise).resolves.toBe("guarded output");
🤖 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 `@packages/core/src/agent/guardrail.integration.spec.ts` around lines 211 -
215, The test for guardrail stream gating is incomplete because it only verifies
the final output after the guardrail is released, not that the stream was
actually blocked before release. Add an assertion between the await
guardrailStarted statement and the releaseGuardrail() call to verify that the
textPromise has not yet resolved, which confirms the stream is being properly
gated and will only proceed after guardrail release. This ensures the
"holdUntilPass" behavior is actually tested rather than just checking the
eventual result.
packages/core/src/agent/agent.ts (1)

437-440: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid any[] for UI replacement chunks.

This factory builds public UI stream chunks, but any[] hides invalid chunk shapes from the compiler. Give the replacement chunk structure a typed alias or make the function generic over the UI stream chunk type instead of casting later. As per coding guidelines, **/*.ts: Maintain type safety in TypeScript-first codebase.

🤖 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 `@packages/core/src/agent/agent.ts` around lines 437 - 440, Replace the `any[]`
return type in the `createInputGuardrailBlockedUIStreamChunks` function with a
properly typed return type. Either create a typed alias for the UI stream chunk
structure that this function returns, or make the function generic over the UI
stream chunk type parameter. This ensures type safety for the stream chunks
being built and eliminates the need for casting elsewhere in the codebase.

Source: Coding guidelines

🤖 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 `@packages/core/src/agent/agent.ts`:
- Around line 2081-2093: A parallel guardrail created in the
SpeculativeInputGuardrailRun can block before streamText finishes its setup,
causing the abort signal to reject the model startup and probing operations
prematurely. Add a blocked-decision check immediately before the model startup
and probing logic begins, and if a block decision already exists from the
guardrail, return the replacement stream result from the buffer instead of
allowing the abort to propagate and reject the call. This ensures that
fast-blocking guardrails are handled before the gated replacement streams are
returned, preventing setup failures.
- Line 6779: The waitForSpeculativeInputGuardrail method throws
guardrail-specific errors that should not be processed as generic tool errors
through handleToolError, which converts them to tool error results. Wrap the
await this.waitForSpeculativeInputGuardrail(oc) calls (at lines 6779 and 6840)
in a try-catch block that identifies and rethrows guardrail block/config errors
before they are caught by the outer exception handler that calls
handleToolError, ensuring guardrail errors bypass the tool error conversion
flow.
- Around line 327-329: The abort call on the abortController in the
decision.error handling block triggers the normal cancellation path through
setupAbortSignalListener which fires onEnd hooks with a cancellation error. This
conflicts with guardrail blocks which should return early without firing final
hooks. Modify the abort logic to avoid triggering the onEnd hooks for guardrail
blocks, either by conditionally skipping the abort call when handling guardrail
errors or by using an alternative mechanism that prevents
setupAbortSignalListener from executing the onEnd hooks in the guardrail block
scenario.
- Around line 375-381: When handling a source-error result type in the code
block checking result.type === "source-error", ensure the guardrail decision is
fully resolved before throwing the error. Currently, getDecision() may return
undefined if the decision is still pending. Add logic to await the guardrail
decision completion if it hasn't been resolved yet, then check if the resolved
decision status is "blocked" to yield the replacement message, otherwise throw
the result.error. This prevents premature error throwing while a guardrail block
decision is still being determined.
- Around line 282-295: The catch block in the input guardrails error handling is
treating all errors as blocked guardrail decisions by unconditionally setting
status to "blocked", which masks configuration or runtime errors like
GUARDRAIL_INPUT_MODIFY_UNSUPPORTED that should be raised to the caller.
Differentiate between actual guardrail blocks (errors with code
"GUARDRAIL_INPUT_BLOCKED") and configuration/runtime errors: only convert
guardrail block errors to a decision with status "blocked", while re-throwing or
propagating configuration errors like GUARDRAIL_INPUT_MODIFY_UNSUPPORTED so they
are not masked as blocked text replacement.

In `@packages/core/src/agent/guardrail.ts`:
- Around line 374-386: The guard against modifying input in parallel mode only
checks when modifiedInput is defined, but a guardrail can still return action:
"modify" without providing modifiedInput and bypass this restriction. In the
guardrail execution block where you check options.allowModify === false, also
verify that the resolvedDecision does not have action: "modify" set. Update the
condition to reject the guardrail execution whenever action is "modify" in
parallel mode, regardless of whether modifiedInput is present or omitted.

---

Outside diff comments:
In `@packages/core/src/agent/agent.ts`:
- Around line 7584-7610: The recordStepResults call is not gated by the
speculative guardrail check, allowing step records to be persisted via
memoryManager.saveConversationSteps even when a speculative guardrail is still
pending. Add the same guardrail condition that protects persistQueue to also
guard the recordStepResults method call within the conversationPersistence.mode
=== "step" block, ensuring that the condition (!speculativeInputGuardrail ||
speculativeInputGuardrail.hasPassed()) is satisfied before calling
recordStepResults.

---

Nitpick comments:
In `@packages/core/src/agent/agent.ts`:
- Around line 437-440: Replace the `any[]` return type in the
`createInputGuardrailBlockedUIStreamChunks` function with a properly typed
return type. Either create a typed alias for the UI stream chunk structure that
this function returns, or make the function generic over the UI stream chunk
type parameter. This ensures type safety for the stream chunks being built and
eliminates the need for casting elsewhere in the codebase.

In `@packages/core/src/agent/guardrail.integration.spec.ts`:
- Around line 211-215: The test for guardrail stream gating is incomplete
because it only verifies the final output after the guardrail is released, not
that the stream was actually blocked before release. Add an assertion between
the await guardrailStarted statement and the releaseGuardrail() call to verify
that the textPromise has not yet resolved, which confirms the stream is being
properly gated and will only proceed after guardrail release. This ensures the
"holdUntilPass" behavior is actually tested rather than just checking the
eventual result.
🪄 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: e4fb843d-e4c9-4903-b800-d44c33f59b6c

📥 Commits

Reviewing files that changed from the base of the PR and between 1e1af65 and 0557489.

📒 Files selected for processing (9)
  • .changeset/parallel-input-guardrails.md
  • packages/core/src/agent/agent.ts
  • packages/core/src/agent/conversation-buffer.spec.ts
  • packages/core/src/agent/conversation-buffer.ts
  • packages/core/src/agent/guardrail.integration.spec.ts
  • packages/core/src/agent/guardrail.spec.ts
  • packages/core/src/agent/guardrail.ts
  • packages/core/src/agent/types.ts
  • website/docs/guardrails/overview.md

Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/agent.ts
Comment thread packages/core/src/agent/agent.ts
Comment thread packages/core/src/agent/guardrail.ts

@cubic-dev-ai cubic-dev-ai 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.

7 issues found across 9 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="packages/core/src/agent/guardrail.ts">

<violation number="1" location="packages/core/src/agent/guardrail.ts:374">
P2: The `allowModify === false` check is only reached when `resolvedDecision.modifiedInput !== undefined`. A guardrail returning `action: "modify"` without providing `modifiedInput` will bypass this validation and silently pass in parallel mode, violating the contract that parallel guardrails can only allow or block. Move the `allowModify` check to trigger on `action === "modify"` regardless of whether `modifiedInput` is set.</violation>
</file>

<file name="packages/core/src/agent/agent.ts">

<violation number="1" location="packages/core/src/agent/agent.ts:282">
P2: All errors thrown by `executeInputGuardrails` are caught and converted to a `blocked` decision. This includes configuration errors like `GUARDRAIL_INPUT_MODIFY_UNSUPPORTED` (thrown when a parallel guardrail returns `action: "modify"`). Instead of surfacing the configuration error to the developer, it will be silently shown to the user as blocked stream text. Distinguish configuration/runtime errors from intentional guardrail blocks and rethrow non-block errors.</violation>

<violation number="2" location="packages/core/src/agent/agent.ts:328">
P2: Aborting the operation context controller here fires the normal cancellation path via `setupAbortSignalListener`, which calls `onEnd` with a cancellation error. This conflicts with the blocked-stream path that intentionally returns early to skip final hooks. The guardrail block abort should be distinguishable from user cancellation to avoid spurious `onEnd` hook invocations.</violation>

<violation number="3" location="packages/core/src/agent/agent.ts:6779">
P2: `waitForSpeculativeInputGuardrail` throws the guardrail block error, but it's inside a `try` block whose `catch` routes all errors through `handleToolError`. This will invoke tool error hooks and emit a tool error result for what is actually a guardrail block. The guardrail error should be rethrown (or the catch should detect it) before the generic tool-error handling path.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/guardrail.ts
Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/agent.ts Outdated
Comment thread packages/core/src/agent/agent.ts
Comment thread packages/core/src/agent/agent.ts Outdated

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/core/src/agent/agent.ts (3)

1886-1888: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Clear already-created feedback state on block.

resolveFeedbackDeferred(null) is a no-op once feedback metadata has already resolved, leaving feedbackValue / feedbackMetadataValue available for a blocked stream. Clear those cached values in onBlock before resolving.

Proposed fix
             buffer,
             onBlock: () => {
+              feedbackMetadataValue = null;
+              feedbackValue = null;
               resolveFeedbackDeferred(null);
             },
           });
🤖 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 `@packages/core/src/agent/agent.ts` around lines 1886 - 1888, In the onBlock
callback handler, before calling resolveFeedbackDeferred(null), clear the cached
feedback state variables feedbackValue and feedbackMetadataValue by setting them
to null or undefined. This ensures that when the feedback deferred has already
been resolved, any previously cached feedback metadata values are properly
cleared and not left available for blocked streams.

1878-1890: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Defer memory writes until the parallel guardrail passes.

The speculative guardrail starts here, but prepareExecution runs immediately afterward and can still write the current input through the semantic-memory queueSaveInput path and the regular prepareConversationContext(..., { persistInput: true }) path before the guardrail decision. A later buffer checkpoint restore cannot undo those external memory writes, so blocked inputs can still be persisted.

Please thread the speculative decision into message preparation and disable/defer input persistence while the guardrail is pending, then persist only after hasPassed()/wait() resolves with passed.

🤖 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 `@packages/core/src/agent/agent.ts` around lines 1878 - 1890, The issue is that
SpeculativeInputGuardrailRun is created and stored in the operation context, but
input persistence through the queueSaveInput path and prepareConversationContext
with persistInput: true flag can still occur before the speculative guardrail
has completed its check. To fix this, you need to defer all input memory writes
until after the guardrail decision is resolved. Thread the
speculativeInputGuardrail decision into the message preparation logic by
checking the guardrail status before executing persistence operations, and
ensure that queueSaveInput and persistInput: true are only called after the
guardrail has passed by checking hasPassed() or waiting for wait() to resolve
with a passed result. This ensures blocked inputs are never persisted to
semantic memory.

7336-7362: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Gate step-history persistence before recordStepResults writes.

The new hasPassed() check only guards persistQueue, but recordStepResults above it can already save step text/tool calls/tool results while the speculative guardrail is pending. If the guardrail later blocks, generated output has still been persisted.

Proposed fix
     return async (event: StepResult<ToolSet>) => {
       const speculativeInputGuardrail = this.getSpeculativeInputGuardrail(oc);
+      const canPersistSpeculativeStep =
+        !speculativeInputGuardrail || speculativeInputGuardrail.hasPassed();
       const { shouldFlushForToolCompletion, bailedResult } = this.processStepContent(oc, event);
@@
-      if (conversationPersistence.mode === "step") {
+      if (conversationPersistence.mode === "step" && canPersistSpeculativeStep) {
         await this.recordStepResults(undefined, oc, {
           awaitPersistence: shouldFlushStepPersistence,
         });
       }
@@
         shouldPersistMemory &&
         persistQueue &&
-        (!speculativeInputGuardrail || speculativeInputGuardrail.hasPassed()) &&
+        canPersistSpeculativeStep &&
         conversationPersistence.mode === "step" &&
         (hasResponseMessages || shouldFlushStepPersistence)
       ) {
🤖 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 `@packages/core/src/agent/agent.ts` around lines 7336 - 7362, The
recordStepResults method call in the block checking if
conversationPersistence.mode === "step" is not being gated by the
speculativeInputGuardrail check, allowing step data to be persisted before the
guardrail completes its validation. Move the entire recordStepResults call block
(the if statement checking conversationPersistence.mode === "step") inside a
condition that also checks either no speculativeInputGuardrail exists or
speculativeInputGuardrail.hasPassed() returns true, ensuring no step history is
persisted if the guardrail later blocks the request.
🧹 Nitpick comments (5)
packages/core/src/agent/streaming/input-guardrail-stream.ts (3)

250-265: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tighten the UI-chunk builder return type.

createInputGuardrailBlockedUIStreamChunks returns any[], which the callsite then re-casts (as UIStreamChunk[]). Typing the elements (e.g. the UI message stream chunk union) would preserve type safety and remove the downstream cast.

As per coding guidelines: "Maintain type safety in TypeScript-first codebase".

🤖 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 `@packages/core/src/agent/streaming/input-guardrail-stream.ts` around lines 250
- 265, The createInputGuardrailBlockedUIStreamChunks function currently returns
any[] which requires downstream casting at call sites, reducing type safety.
Change the return type from any[] to UIStreamChunk[] (or the appropriate UI
stream chunk union type) to properly type the returned array of stream chunk
objects. This will eliminate the need for downstream as UIStreamChunk[] casts
and maintain type safety throughout the codebase.

Source: Coding guidelines


157-223: 🚀 Performance & Scalability | 🔵 Trivial

Gating buffers the entire source with no backpressure.

Within a single consumer pull, the while (true) loop keeps calling iterator.next() and pushing into buffered without yielding until the guardrail resolves. For a fast model stream paired with a slow (e.g. LLM-based) holdUntilPass guardrail, this drains and holds the full response in memory, and applies no backpressure to the source. This is acceptable for typical short responses but can cause memory pressure for large/long-running streams.

Consider bounding the buffer (cap size / abort + replace when exceeded) or documenting the upper bound so operators understand the memory envelope under slow guardrails.

🤖 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 `@packages/core/src/agent/streaming/input-guardrail-stream.ts` around lines 157
- 223, The gateIterableUntilSpeculativeInputPass function buffers all items from
the source iterator into the buffered array without any size limit or
backpressure mechanism. When the guardrail is slow to resolve (like a
holdUntilPass guardrail), the while true loop continuously calls iterator.next()
and pushes items into the buffered array, causing all data to be held in memory.
Fix this by implementing a buffer size limit in the
gateIterableUntilSpeculativeInputPass function: add a configurable maximum
buffer size parameter, check the buffered array length before pushing new items,
and when the limit is reached, either pause consuming from the iterator until
items are yielded, or abort the operation and use the replacement function.
Alternatively, if bounding is not feasible, add clear documentation about the
memory behavior and upper bound expectations for operators using this function
with slow guardrails.

116-122: 🩺 Stability & Availability | 🔵 Trivial

Function signature misleads about input type; however, actual callers always provide AsyncIterableStream.

The function accepts AsyncIterable<string> but both call sites in agent.ts (lines 2631, 2636) pass AsyncIterableStream<string>: guardrailPipeline.textStream (explicitly AsyncIterableStream<string> per GuardrailPipeline interface) and result.textStream (from streamText in the "ai" SDK). The cast at line 121 is therefore safe in practice since the input is already the correct type.

However, the function signature doesn't reflect actual usage. Consider either:

  • Changing the parameter type to AsyncIterableStream<string> to match reality, or
  • Adding runtime wrapping via iterableToStream for any plain AsyncIterable<string> inputs to maintain the current signature's generality.
🤖 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 `@packages/core/src/agent/streaming/input-guardrail-stream.ts` around lines 116
- 122, The function applySpeculativeInputGuardrailToTextStream accepts
AsyncIterable<string> in its params parameter but all actual callers pass
AsyncIterableStream<string>. Update the baseStream property type in the params
object from AsyncIterable<string> to AsyncIterableStream<string> to accurately
reflect the actual usage pattern and eliminate the misleading type signature.
This makes the function contract honest about what it expects to receive from
its callers.
packages/core/src/agent/guardrail.integration.spec.ts (1)

345-345: 📐 Maintainability & Code Quality | 🔵 Trivial

Remove explicit <any> type parameter to preserve type inference.

Line 345 uses collectStream<any>(...) which bypasses TypeScript's type checking. Removing the explicit type parameter allows the compiler to infer the correct type from uiStreamResult.toUIMessageStream(), maintaining type safety without reducing functionality.

Suggested fix
-    const uiChunks = await collectStream<any>(uiStreamResult.toUIMessageStream());
+    const uiChunks = await collectStream(uiStreamResult.toUIMessageStream());
🤖 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 `@packages/core/src/agent/guardrail.integration.spec.ts` at line 345, The
collectStream function call at line 345 uses an explicit `<any>` type parameter
which bypasses TypeScript's type checking and prevents proper type inference.
Remove the `<any>` type parameter from the collectStream call so that TypeScript
can automatically infer the correct type from the
uiStreamResult.toUIMessageStream() return type. This change maintains
functionality while improving type safety by allowing the compiler to determine
the appropriate type for uiChunks instead of accepting any type.

Source: Coding guidelines

packages/core/src/agent/agent.spec.ts (1)

1673-1688: 📐 Maintainability & Code Quality | 🔵 Trivial

Remove as any casts from the new speculative guardrail tests.

Lines 1673, 1682, 1687, 1709, 1719, and 1728 use as any, which weakens type safety. For accessing private Agent methods, use a narrow test-only typed helper. For the model, prefer a concrete type assertion. This aligns with the TypeScript-first guideline to maintain type safety in **/*.ts files.

🤖 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 `@packages/core/src/agent/agent.spec.ts` around lines 1673 - 1688, Remove the
`as any` type casts used throughout the speculative guardrail tests (appearing
in the mockModel assignment, and on calls to createOperationContext and
createToolExecutionFactory) by creating a narrow test-only typed helper for
accessing private Agent methods and using concrete type assertions for the
mockModel instead of broad `as any` casts. This maintains type safety in the
test file and aligns with TypeScript-first guidelines.

Source: Coding guidelines

🤖 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 `@packages/core/src/agent/agent.ts`:
- Around line 2616-2656: The `partialOutputStream` is not being protected by the
speculative input guardrail like the other streams (fullStream, textStream, and
UIStream). Create a new wrapper function
`getGuardrailAwarePartialOutputStream()` following the same pattern as
`getGuardrailAwareFullStream`, `getGuardrailAwareTextStream`, and
`getGuardrailAwareUIStream` that applies the speculative input guardrail to the
partial output stream, checking if guardrailPipeline exists and applying the
guardrail appropriately. Then update the `partialOutputStream` getter to return
the result of `getGuardrailAwarePartialOutputStream()` instead of delegating
directly.

---

Outside diff comments:
In `@packages/core/src/agent/agent.ts`:
- Around line 1886-1888: In the onBlock callback handler, before calling
resolveFeedbackDeferred(null), clear the cached feedback state variables
feedbackValue and feedbackMetadataValue by setting them to null or undefined.
This ensures that when the feedback deferred has already been resolved, any
previously cached feedback metadata values are properly cleared and not left
available for blocked streams.
- Around line 1878-1890: The issue is that SpeculativeInputGuardrailRun is
created and stored in the operation context, but input persistence through the
queueSaveInput path and prepareConversationContext with persistInput: true flag
can still occur before the speculative guardrail has completed its check. To fix
this, you need to defer all input memory writes until after the guardrail
decision is resolved. Thread the speculativeInputGuardrail decision into the
message preparation logic by checking the guardrail status before executing
persistence operations, and ensure that queueSaveInput and persistInput: true
are only called after the guardrail has passed by checking hasPassed() or
waiting for wait() to resolve with a passed result. This ensures blocked inputs
are never persisted to semantic memory.
- Around line 7336-7362: The recordStepResults method call in the block checking
if conversationPersistence.mode === "step" is not being gated by the
speculativeInputGuardrail check, allowing step data to be persisted before the
guardrail completes its validation. Move the entire recordStepResults call block
(the if statement checking conversationPersistence.mode === "step") inside a
condition that also checks either no speculativeInputGuardrail exists or
speculativeInputGuardrail.hasPassed() returns true, ensuring no step history is
persisted if the guardrail later blocks the request.

---

Nitpick comments:
In `@packages/core/src/agent/agent.spec.ts`:
- Around line 1673-1688: Remove the `as any` type casts used throughout the
speculative guardrail tests (appearing in the mockModel assignment, and on calls
to createOperationContext and createToolExecutionFactory) by creating a narrow
test-only typed helper for accessing private Agent methods and using concrete
type assertions for the mockModel instead of broad `as any` casts. This
maintains type safety in the test file and aligns with TypeScript-first
guidelines.

In `@packages/core/src/agent/guardrail.integration.spec.ts`:
- Line 345: The collectStream function call at line 345 uses an explicit `<any>`
type parameter which bypasses TypeScript's type checking and prevents proper
type inference. Remove the `<any>` type parameter from the collectStream call so
that TypeScript can automatically infer the correct type from the
uiStreamResult.toUIMessageStream() return type. This change maintains
functionality while improving type safety by allowing the compiler to determine
the appropriate type for uiChunks instead of accepting any type.

In `@packages/core/src/agent/streaming/input-guardrail-stream.ts`:
- Around line 250-265: The createInputGuardrailBlockedUIStreamChunks function
currently returns any[] which requires downstream casting at call sites,
reducing type safety. Change the return type from any[] to UIStreamChunk[] (or
the appropriate UI stream chunk union type) to properly type the returned array
of stream chunk objects. This will eliminate the need for downstream as
UIStreamChunk[] casts and maintain type safety throughout the codebase.
- Around line 157-223: The gateIterableUntilSpeculativeInputPass function
buffers all items from the source iterator into the buffered array without any
size limit or backpressure mechanism. When the guardrail is slow to resolve
(like a holdUntilPass guardrail), the while true loop continuously calls
iterator.next() and pushes items into the buffered array, causing all data to be
held in memory. Fix this by implementing a buffer size limit in the
gateIterableUntilSpeculativeInputPass function: add a configurable maximum
buffer size parameter, check the buffered array length before pushing new items,
and when the limit is reached, either pause consuming from the iterator until
items are yielded, or abort the operation and use the replacement function.
Alternatively, if bounding is not feasible, add clear documentation about the
memory behavior and upper bound expectations for operators using this function
with slow guardrails.
- Around line 116-122: The function applySpeculativeInputGuardrailToTextStream
accepts AsyncIterable<string> in its params parameter but all actual callers
pass AsyncIterableStream<string>. Update the baseStream property type in the
params object from AsyncIterable<string> to AsyncIterableStream<string> to
accurately reflect the actual usage pattern and eliminate the misleading type
signature. This makes the function contract honest about what it expects to
receive from its callers.
🪄 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: 9fb3ff74-88b4-45b8-8714-282fe98733ba

📥 Commits

Reviewing files that changed from the base of the PR and between 0557489 and 4ff3900.

📒 Files selected for processing (7)
  • .changeset/parallel-input-guardrails.md
  • packages/core/src/agent/agent.spec.ts
  • packages/core/src/agent/agent.ts
  • packages/core/src/agent/context-keys.ts
  • packages/core/src/agent/guardrail.integration.spec.ts
  • packages/core/src/agent/streaming/input-guardrail-stream.ts
  • website/docs/guardrails/overview.md
✅ Files skipped from review due to trivial changes (1)
  • .changeset/parallel-input-guardrails.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/docs/guardrails/overview.md

Comment thread packages/core/src/agent/agent.ts

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/agent/streaming/input-guardrail-stream.ts (1)

243-259: 🎯 Functional Correctness | 🟠 Major

Remove the non-standard delta field from text-delta and add required rawFinishReason and totalUsage to the finish part to match ai SDK v6.0.0 TextStreamPart shape.

These objects flow into fullStream and are cast to VoltAgentTextStreamPart (which extends ai's TextStreamPart). In ai@6.0.0:

  • text-delta expects only { type, id, text } — the delta field is not recognized and will be ignored by consumers.
  • finish requires { type, finishReason, rawFinishReason, totalUsage } — both rawFinishReason and totalUsage are missing. Consumers computing token usage on this error path will break or return incomplete data.
Current shape (incorrect)
{
  type: "text-delta",
  id: textId,
  delta: data.message,        // ← not in ai@6.0.0 TextStreamPart
  text: data.message,
}
{
  type: "finish",
  finishReason: "error",      // ← missing rawFinishReason and totalUsage
}
🤖 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 `@packages/core/src/agent/streaming/input-guardrail-stream.ts` around lines 243
- 259, In the stream parts returned in the input guardrail blocking logic around
the text-delta and finish objects, remove the non-standard delta field from the
text-delta part (keeping only type, id, and text fields), and add the required
rawFinishReason and totalUsage fields to the finish part object to align with ai
SDK v6.0.0 TextStreamPart shape expectations. These fields are necessary for
consumers that depend on complete token usage and finish reason information.
🧹 Nitpick comments (1)
packages/core/src/agent/streaming/input-guardrail-stream.ts (1)

263-282: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tighten the any[] return type.

createInputGuardrailBlockedUIStreamChunks returns any[], which erases all type checking on the emitted UI chunks and on the INPUT_GUARDRAIL_BLOCKED_UI_EVENT_TYPE payload. Consider a discriminated union (e.g. a UIStreamChunk type) so the start/event/text/finish chunk shapes are validated at compile time.

As per coding guidelines: "Maintain type safety in TypeScript-first codebase".

🤖 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 `@packages/core/src/agent/streaming/input-guardrail-stream.ts` around lines 263
- 282, The function createInputGuardrailBlockedUIStreamChunks currently returns
any[] which eliminates type safety for the emitted UI chunks. Define a
discriminated union type (such as UIStreamChunk) that explicitly describes the
shape of each chunk type in the array (start, event, text-start, text-delta,
text-end, and finish), capturing the specific properties and payload shapes for
each. Then replace the any[] return type annotation with this new discriminated
union type or an array of it to ensure compile-time validation of all chunk
shapes and the INPUT_GUARDRAIL_BLOCKED_UI_EVENT_TYPE payload structure.

Source: Coding guidelines

🤖 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 `@packages/core/src/agent/subagent/types.ts`:
- Around line 195-199: The VoltAgentTextStreamPart type now includes
InputGuardrailBlockedStreamPart, but the default forwarding allowlist that
controls which stream events are propagated to the parent agent does not include
the new "input-guardrail-blocked" type. Locate the default forwarding allowlist
(which currently contains only "tool-call" and "tool-result") and add
"input-guardrail-blocked" to this allowlist to ensure guardrail-blocked events
are properly forwarded instead of being silently dropped during subagent stream
forwarding.

---

Outside diff comments:
In `@packages/core/src/agent/streaming/input-guardrail-stream.ts`:
- Around line 243-259: In the stream parts returned in the input guardrail
blocking logic around the text-delta and finish objects, remove the non-standard
delta field from the text-delta part (keeping only type, id, and text fields),
and add the required rawFinishReason and totalUsage fields to the finish part
object to align with ai SDK v6.0.0 TextStreamPart shape expectations. These
fields are necessary for consumers that depend on complete token usage and
finish reason information.

---

Nitpick comments:
In `@packages/core/src/agent/streaming/input-guardrail-stream.ts`:
- Around line 263-282: The function createInputGuardrailBlockedUIStreamChunks
currently returns any[] which eliminates type safety for the emitted UI chunks.
Define a discriminated union type (such as UIStreamChunk) that explicitly
describes the shape of each chunk type in the array (start, event, text-start,
text-delta, text-end, and finish), capturing the specific properties and payload
shapes for each. Then replace the any[] return type annotation with this new
discriminated union type or an array of it to ensure compile-time validation of
all chunk shapes and the INPUT_GUARDRAIL_BLOCKED_UI_EVENT_TYPE payload
structure.
🪄 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: 2e642599-b59d-43b0-9a03-e9541682509e

📥 Commits

Reviewing files that changed from the base of the PR and between 4ff3900 and 6eb7ee4.

📒 Files selected for processing (8)
  • .changeset/parallel-input-guardrails.md
  • packages/core/src/agent/guardrail.integration.spec.ts
  • packages/core/src/agent/guardrail.spec.ts
  • packages/core/src/agent/guardrail.ts
  • packages/core/src/agent/streaming/input-guardrail-stream.ts
  • packages/core/src/agent/subagent/types.ts
  • packages/core/src/index.ts
  • website/docs/guardrails/overview.md
✅ Files skipped from review due to trivial changes (2)
  • .changeset/parallel-input-guardrails.md
  • website/docs/guardrails/overview.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/agent/guardrail.integration.spec.ts
  • packages/core/src/agent/guardrail.ts
  • packages/core/src/agent/guardrail.spec.ts

Comment thread packages/core/src/agent/subagent/types.ts

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 8 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/core/src/agent/subagent/types.ts
@omeraplak
omeraplak merged commit e108ba7 into main Jun 23, 2026
22 of 23 checks passed
@omeraplak
omeraplak deleted the feature/speculative-input-guardrails branch June 23, 2026 03:42
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