Skip to content

Harden Code Mode runtime contracts#668

Merged
urjitc merged 5 commits into
mainfrom
agent/codemode-runtime-contracts
Jul 19, 2026
Merged

Harden Code Mode runtime contracts#668
urjitc merged 5 commits into
mainfrom
agent/codemode-runtime-contracts

Conversation

@urjitc

@urjitc urjitc commented Jul 19, 2026

Copy link
Copy Markdown
Member

What changed

  • add one app-owned, schema-validated execution module shared by direct AI SDK and nested Code Mode calls
  • replace fabricated nested AI SDK options with an honest ThinkEx execution context
  • fail closed on malformed Code Mode envelopes, child calls, and tool outputs
  • aggregate nested semantic outcomes and send only compact, redacted orchestration telemetry to PostHog
  • give every first-party nested tool concrete input and output types, including a discriminated research-deepen input
  • validate MCP operation inputs and outputs at the canonical operation seam
  • retain the 100k compaction policy while owning a Pi-style continuation checkpoint and safe structured-result serialization
  • centralize model/UI tool policy and remove repeated presentation derivation in chat rendering

Why

@cloudflare/codemode currently invokes AI SDK tools without ToolExecutionOptions, which caused nested workspace calls to throw while the outer orchestration span could still be recorded as successful. AI SDK outputSchema also does not validate ordinary direct execution results at runtime.

This keeps Cloudflare's runtime intact and adds a temporary source-level adapter rather than forking Code Mode or patching compiled compaction output. The adapter can be deleted when upstream supports execution-context propagation and output validation.

Impact

  • direct and nested first-party tools now enforce the same runtime contracts
  • nested workspace operations receive stable invocation IDs without fake message history
  • malformed orchestration results cannot become successful UI or telemetry outcomes
  • raw nested arguments, results, and final model output are excluded from PostHog projections
  • persisted legacy tool transcripts remain renderable

Validation

  • pnpm check
  • pnpm test — 97/97
  • pnpm test:workers — 15/15
  • pnpm knip
  • npx react-doctor@latest --verbose --scope changed — no changed-code issues
  • pnpm build:app — application build, four local container images, and /blog prerender completed

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added richer AI thread orchestration with clearer tool statuses, outcomes, summaries, and telemetry.
    • Improved AI tool activity displays with consistent titles, icons, and visibility handling.
    • Added structured compaction support that preserves tool inputs and outputs in summaries.
    • Added stronger validation and typed results for workspace, web search, research, and compute tools.
  • Bug Fixes

    • Invalid tool and orchestration results are now rejected or reported as clear errors.
    • Hidden nested tools no longer appear in chat activity.
  • Tests

    • Expanded coverage for validation, orchestration, compaction, tool presentation, and generated AI types.

@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@capy-ai

capy-ai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0aff2fb9-dc59-486c-917b-ee146620572d

📥 Commits

Reviewing files that changed from the base of the PR and between a6212b4 and f99659b.

📒 Files selected for processing (10)
  • src/features/mcp/mcp-operation-catalog.worker.test.ts
  • src/features/workspaces/ai/ai-codemode-types.worker.test.ts
  • src/features/workspaces/ai/ai-compaction.test.ts
  • src/features/workspaces/ai/ai-thread-orchestration-contract.ts
  • src/features/workspaces/ai/ai-thread-orchestration.worker.test.ts
  • src/features/workspaces/ai/ai-thread-runtime.ts
  • src/features/workspaces/ai/ai-tool-outcome.ts
  • src/features/workspaces/ai/ai-tool-registry.test.ts
  • src/features/workspaces/ai/ai-tool-registry.ts
  • src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx
📝 Walkthrough

Walkthrough

The change centralizes MCP schema validation, adds validated AI thread tool execution and orchestration normalization, introduces a shared AI tool registry, updates compaction and telemetry handling, and propagates structured tool presentation metadata through chat activity rendering.

Changes

MCP operation validation

Layer / File(s) Summary
MCP operation schema wiring
src/features/mcp/mcp-operation-catalog.ts, src/features/mcp/mcp-operation-catalog.worker.test.ts
MCP operations use shared input/output schema parsing, workspace context derives from typed input, and tests cover invalid outputs and model-access alignment.

AI thread tooling modernization

Layer / File(s) Summary
Validated AI tool contracts
src/features/workspaces/ai/ai-thread-tool.ts, src/features/workspaces/ai/*-tools.ts, src/integrations/firecrawl/*, src/features/workspaces/ai/ai-thread-tool.test.ts, src/features/workspaces/ai/ai-codemode-types.worker.test.ts
AI tools use shared validated execution with explicit output schemas, while research inputs and Firecrawl outputs use schema-derived types.
Tool registry and runtime catalog
src/features/workspaces/ai/ai-tool-registry.ts, src/features/workspaces/ai/ai-thread-runtime.ts, src/features/workspaces/ai/ai-tool-registry.test.ts
Model access, Code Mode settings, and UI presentation are centralized in a registry used to construct runtime tool entries.
Orchestration and outcome normalization
src/features/workspaces/ai/ai-thread-orchestration*.ts, src/features/workspaces/ai/ai-tool-outcome*.ts, src/features/workspaces/ai/ai-thread-posthog-recorder.ts
Nested execution is connected through a Code Mode connector, orchestration results are normalized and aggregated, and telemetry omits final orchestration results.
AI thread compaction pipeline
src/features/workspaces/ai/ai-compaction.ts, src/features/workspaces/ai/ai-compaction.test.ts, src/features/workspaces/ai/ai-thread.ts
Tool outputs are serialized before compaction and summarization uses a dedicated system prompt.
Tool presentation in chat activities
src/features/workspaces/components/ai-chat/*, src/features/workspaces/ai/ai-tool-registry.ts
Chat activities carry registry-derived presentation metadata, filter hidden nested tools, and render structured titles and icons.

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

Sequence Diagram(s)

sequenceDiagram
  participant AIThreadRuntime
  participant OrchestrationTool
  participant NestedToolConnector
  participant OutcomeNormalizer
  AIThreadRuntime->>OrchestrationTool: execute orchestration
  OrchestrationTool->>NestedToolConnector: expose and execute nested tools
  NestedToolConnector-->>OrchestrationTool: return nested results
  OrchestrationTool->>OutcomeNormalizer: normalize orchestration output
  OutcomeNormalizer-->>AIThreadRuntime: return calls and aggregated outcome
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.56% 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 clearly summarizes the main change: strengthening Code Mode runtime contracts.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/codemode-runtime-contracts

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.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit f99659b.

@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR hardens Code Mode and AI tool runtime contracts. The main changes are:

  • A shared validated AI thread tool runtime for direct and nested execution.
  • Normalized Code Mode orchestration results with aggregated outcomes and redacted telemetry.
  • Schema validation at MCP, workspace, web, research, time, and compute tool seams.
  • Centralized AI tool model policy and UI presentation metadata.
  • Updated chat rendering for durable Code Mode child activity logs.
  • Safer conversation compaction with structured tool-result serialization.

Confidence Score: 5/5

This PR appears safe to merge with low risk.

The changed runtime seams validate inputs and outputs, normalize malformed orchestration results into errors, and include targeted tests for the main contract, telemetry, UI, and compaction paths.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the contract-validation workflow and observed a PNPM check failure due to formatting issues in generated files.
  • Reviewed the targeted Node Vitest run and confirmed it exited with code 0, with 4 test files passed and 10 tests passed.
  • Reviewed the targeted Cloudflare Worker Vitest run and confirmed it exited with code 0, with 4 test files passed and 15 tests passed, despite non-blocking missing-secret warnings.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/features/workspaces/ai/ai-thread-tool.ts Introduces the shared AI thread tool definition helper with runtime input/output validation and honest execution context.
src/features/workspaces/ai/ai-thread-orchestration.ts Wraps Code Mode execution with app-owned output normalization and nested tool runtime validation.
src/features/workspaces/ai/ai-thread-orchestration-contract.ts Defines normalization, validation, outcome aggregation, and redacted telemetry projection for Code Mode execution results.
src/features/workspaces/ai/ai-thread-runtime.ts Centralizes tool model policy via the registry and swaps direct Code Mode execution for the new orchestration wrapper.
src/features/workspaces/ai/ai-tool-registry.ts Centralizes model access, Code Mode eligibility, UI labels, icons, and visibility for AI tools.
src/features/mcp/mcp-operation-catalog.ts Adds schema-backed MCP operation definitions so inputs and outputs are validated at the catalog execution seam.
src/features/workspaces/components/ai-chat/ai-chat-display-state.ts Groups Code Mode tool activity from either durable call logs or legacy sibling tool parts.
src/features/workspaces/components/ai-chat/ai-chat-codemode-activity.ts Normalizes durable Code Mode call logs and compact child activities into UI activity rows.
src/features/workspaces/ai/ai-compaction.ts Introduces a custom compaction prompt and adapter that stringifies structured tool outputs before Agents compaction.
src/integrations/firecrawl/research.ts Adds zod result schemas and normalized research discovery/deepen response shapes.

Reviews (1): Last reviewed commit: "refactor(ai-chat): centralize tool prese..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6212b450d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

execute: async (args, context) => {
return runtime.execute(args, {
codemodeExecutionId: context?.executionId,
invocationId: crypto.randomUUID(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use deterministic IDs for nested workspace operations

When Code Mode invokes a nested workspace mutation more than once for the same logical connector call, this generates a fresh invocationId each time. The workspace tools pass that value through as the operation ID, and several mutations use it as their clientMutationId, so recovery/retry after the side effect but before the Code Mode result is durably logged can no longer be deduped and may apply the mutation again or turn the retry into a conflict. Derive the ID from the Code Mode execution/call identity instead of fresh randomness.

Useful? React with 👍 / 👎.

@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 29 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="src/features/workspaces/ai/ai-thread-orchestration-contract.ts">

<violation number="1" location="src/features/workspaces/ai/ai-thread-orchestration-contract.ts:122">
P2: `getOrchestrationCallOutcome` treats the `"executing"` state as an error outcome (`failureCodes: ["codemode_tool_error"], failedCount: 1, status: "error"`), while `getOrchestrationCallStatus` maps the same `"executing"` state to `status: "running"`. This produces a self-contradictory normalized call where `state: "executing"`, `status: "running"`, but `outcome: { status: "error", failureCodes: ["codemode_tool_error"], ... }`. If Code Mode ever includes an executing-session call in a finalized output, consumers would see a call reported as still `"running"` while the outcome claims `"error"`.

Consider adding an explicit `"executing"` branch to `getOrchestrationCallOutcome` that returns a neutral outcome (e.g., `{ failureCodes: [], failedCount: 0, status: "success" }` or adding `"executing"` to the state→status mapping so both functions agree.</violation>
</file>

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

Re-trigger cubic

Comment thread src/features/workspaces/ai/ai-thread-orchestration-contract.ts
const childOutcome = aggregateAIToolOutcomes(calls.map((call) => call.outcome));

if (parsed.data.status === "completed") {
return {

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.

P2: getOrchestrationCallOutcome treats the "executing" state as an error outcome (failureCodes: ["codemode_tool_error"], failedCount: 1, status: "error"), while getOrchestrationCallStatus maps the same "executing" state to status: "running". This produces a self-contradictory normalized call where state: "executing", status: "running", but outcome: { status: "error", failureCodes: ["codemode_tool_error"], ... }. If Code Mode ever includes an executing-session call in a finalized output, consumers would see a call reported as still "running" while the outcome claims "error".

Consider adding an explicit "executing" branch to getOrchestrationCallOutcome that returns a neutral outcome (e.g., { failureCodes: [], failedCount: 0, status: "success" } or adding "executing" to the state→status mapping so both functions agree.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/features/workspaces/ai/ai-thread-orchestration-contract.ts, line 122:

<comment>`getOrchestrationCallOutcome` treats the `"executing"` state as an error outcome (`failureCodes: ["codemode_tool_error"], failedCount: 1, status: "error"`), while `getOrchestrationCallStatus` maps the same `"executing"` state to `status: "running"`. This produces a self-contradictory normalized call where `state: "executing"`, `status: "running"`, but `outcome: { status: "error", failureCodes: ["codemode_tool_error"], ... }`. If Code Mode ever includes an executing-session call in a finalized output, consumers would see a call reported as still `"running"` while the outcome claims `"error"`.

Consider adding an explicit `"executing"` branch to `getOrchestrationCallOutcome` that returns a neutral outcome (e.g., `{ failureCodes: [], failedCount: 0, status: "success" }` or adding `"executing"` to the state→status mapping so both functions agree.</comment>

<file context>
@@ -0,0 +1,265 @@
+	const childOutcome = aggregateAIToolOutcomes(calls.map((call) => call.outcome));
+
+	if (parsed.data.status === "completed") {
+		return {
+			status: parsed.data.status,
+			executionId: parsed.data.executionId,
</file context>

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
src/features/workspaces/components/ai-chat/ai-chat-display-state.ts (1)

184-206: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Legacy codemode children bypass hidden-tool filtering.

getCodemodeCallActivities (the compact/call-log path) filters children by presentation.visibility === "visible", but getLegacyCodemodeChildren — the fallback used whenever codemodePart.output isn't in that compact shape — does not. Hidden tools like sandbox_bash, time_get_current, time_calculate_relative, and workspace_link_items will still surface as children in legacy-rendered threads, contradicting the intended "filter hidden nested tools" behavior for those tools, and this gap isn't covered by the new registry-visibility test either.

🐛 Proposed fix
 function getLegacyCodemodeChildren(
 	parts: AiChatMessagePart[],
 	codemodePart: AiChatToolPart,
 ): AiChatToolChildActivity[] {
 	return parts.flatMap((part) => {
 		if (!isToolUIPart(part) || part === codemodePart || !isVisibleToolPart(part)) {
 			return [];
 		}

 		const activity = getToolActivityForPart(part);
-		return activity
+		return activity && activity.presentation.visibility === "visible"
 			? [
 					{
 						id: part.toolCallId,
 						presentation: activity.presentation,
 						status: activity.status,
 						summary: activity.summary,
 						toolName: activity.toolName,
 					},
 				]
 			: [];
 	});
 }
🤖 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 `@src/features/workspaces/components/ai-chat/ai-chat-display-state.ts` around
lines 184 - 206, Update getLegacyCodemodeChildren to exclude child activities
whose presentation visibility is not "visible", matching the filtering behavior
in getCodemodeCallActivities. Apply this check after obtaining each tool
activity and preserve the existing empty-result behavior for hidden or
unavailable activities.
🧹 Nitpick comments (3)
src/features/workspaces/ai/ai-compaction.test.ts (1)

61-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding a standard tool-result part to the test fixture.

The test currently verifies dynamic-tool parts. It would be beneficial to add a native AI SDK tool-result part (which doesn't have an output field) to ensure the adapter logic consistently stringifies standard outputs as well.

🛠️ Proposed test addition
 					{
 						output: undefined,
 						result: { accepted: true },
 						toolCallId: "legacy-tool-call",
 						toolName: "legacy_tool",
 						type: "dynamic-tool",
 					},
+					{
+						result: { nested: "object" },
+						toolCallId: "standard-tool-call",
+						toolName: "standard_tool",
+						type: "tool-result",
+					},
 				],
 				role: "assistant",
 			},
 			message("middle", [{ type: "text", text: "middle" }]),
 			message("tail", [{ type: "text", text: "tail" }]),
 		] as never);
 
 		expect(prompt).toContain('Input: {"path":"/workspace/report"}');
 		expect(prompt).toContain('Output: {"content":"');
 		expect(prompt).toContain('Output: {"accepted":true}');
+		expect(prompt).toContain('{"nested":"object"}');
 		expect(prompt).not.toContain("[object Object]");
🤖 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 `@src/features/workspaces/ai/ai-compaction.test.ts` around lines 61 - 76,
Extend the fixture in the compaction test around the existing dynamic-tool part
with a native AI SDK tool-result part that has no output field, then add an
assertion for its stringified result in prompt. Keep the existing dynamic-tool
assertions and ensure the new case verifies standard tool-result handling
without producing “[object Object]”.
src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx (1)

299-313: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: make the icon switch exhaustive.

The default branch is currently unreachable given AiToolActivityIconKind's closed union, so a future addition to that union would silently render Globe2 instead of failing a type check.

♻️ Optional exhaustiveness guard
 function ToolActivityIcon({ icon }: { icon: AiToolActivityIconKind }) {
 	switch (icon) {
 		case "code":
 			return <Code2 className="size-3.5" aria-hidden="true" />;
 		case "edit":
 			return <PencilLine className="size-3.5" aria-hidden="true" />;
 		case "file":
 			return <FileText className="size-3.5" aria-hidden="true" />;
 		case "search":
 			return <Search className="size-3.5" aria-hidden="true" />;
 		case "web":
 			return <Globe2 className="size-3.5" aria-hidden="true" />;
-		default:
-			return <Globe2 className="size-3.5" aria-hidden="true" />;
+		default: {
+			icon satisfies never;
+			return <Globe2 className="size-3.5" aria-hidden="true" />;
+		}
 	}
 }
🤖 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 `@src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx` around
lines 299 - 313, Make the switch in ToolActivityIcon exhaustive for the
AiToolActivityIconKind union by removing the fallback Globe2 default and adding
the project’s standard unreachable-case or exhaustive-check pattern. Ensure any
future union member causes a type-check failure rather than silently rendering
an unrelated icon.
src/features/workspaces/ai/ai-thread-orchestration-contract.ts (1)

190-196: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated invalid_orchestration_result sentinel across two files. Both files independently hard-code the identical {failureCodes: ["invalid_orchestration_result"], failedCount: 1, status: "error"} literal for the "invalid orchestration output" case; a future change to this sentinel is easy to apply in only one place and silently diverge.

  • src/features/workspaces/ai/ai-thread-orchestration-contract.ts#L190-L196: export invalidOutcome() (or a similarly named constant) from this module instead of keeping it file-private.
  • src/features/workspaces/ai/ai-tool-outcome.ts#L24-L28: import and reuse the exported sentinel from ai-thread-orchestration-contract.ts instead of re-declaring the literal inline.
🤖 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 `@src/features/workspaces/ai/ai-thread-orchestration-contract.ts` around lines
190 - 196, Export the invalidOutcome sentinel from
ai-thread-orchestration-contract.ts, preserving its existing value. In
src/features/workspaces/ai/ai-tool-outcome.ts lines 24-28, import and reuse
invalidOutcome instead of declaring the duplicate literal inline.
🤖 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 `@src/features/workspaces/ai/ai-compaction.ts`:
- Around line 89-100: Update the mapping logic in ai-compaction to stringify
both native result and output fields on tool-result parts. Preserve the existing
value selection and unchanged-part behavior, but when a value exists, overwrite
result alongside output with stringifyForCompaction so downstream summarization
cannot render objects as [object Object].

In `@src/features/workspaces/ai/ai-thread-orchestration-contract.ts`:
- Around line 233-251: The getOrchestrationCallOutcome function currently treats
executing as an error. Add an executing branch that returns the non-error
running outcome expected by aggregateAIToolOutcomes, and add regression coverage
for both executing and paused orchestration paths while preserving existing
applied, pending, reverted, and error behavior.

In `@src/features/workspaces/ai/ai-thread-runtime.ts`:
- Around line 240-251: Add registry coverage in ai-tool-registry.test.ts that
invokes each tool factory used by addAIThreadToolEntries—createSandboxTools,
createAIThreadCodeRunTools, createAIThreadWebTools, createAIThreadResearchTools,
createAIThreadTimeTools, and createAIThreadWorkspaceTools—with minimal or mocked
inputs, then verifies requireAiToolDefinition does not throw for every returned
tool key. Keep the test focused on detecting missing AI_TOOL_REGISTRY entries
during CI.

---

Outside diff comments:
In `@src/features/workspaces/components/ai-chat/ai-chat-display-state.ts`:
- Around line 184-206: Update getLegacyCodemodeChildren to exclude child
activities whose presentation visibility is not "visible", matching the
filtering behavior in getCodemodeCallActivities. Apply this check after
obtaining each tool activity and preserve the existing empty-result behavior for
hidden or unavailable activities.

---

Nitpick comments:
In `@src/features/workspaces/ai/ai-compaction.test.ts`:
- Around line 61-76: Extend the fixture in the compaction test around the
existing dynamic-tool part with a native AI SDK tool-result part that has no
output field, then add an assertion for its stringified result in prompt. Keep
the existing dynamic-tool assertions and ensure the new case verifies standard
tool-result handling without producing “[object Object]”.

In `@src/features/workspaces/ai/ai-thread-orchestration-contract.ts`:
- Around line 190-196: Export the invalidOutcome sentinel from
ai-thread-orchestration-contract.ts, preserving its existing value. In
src/features/workspaces/ai/ai-tool-outcome.ts lines 24-28, import and reuse
invalidOutcome instead of declaring the duplicate literal inline.

In `@src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx`:
- Around line 299-313: Make the switch in ToolActivityIcon exhaustive for the
AiToolActivityIconKind union by removing the fallback Globe2 default and adding
the project’s standard unreachable-case or exhaustive-check pattern. Ensure any
future union member causes a type-check failure rather than silently rendering
an unrelated icon.
🪄 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: b1b2efa6-e176-4c5e-9f28-361d680a2e02

📥 Commits

Reviewing files that changed from the base of the PR and between 8531209 and a6212b4.

📒 Files selected for processing (29)
  • src/features/mcp/mcp-operation-catalog.ts
  • src/features/mcp/mcp-operation-catalog.worker.test.ts
  • src/features/workspaces/ai/ai-codemode-types.worker.test.ts
  • src/features/workspaces/ai/ai-compaction.test.ts
  • src/features/workspaces/ai/ai-compaction.ts
  • src/features/workspaces/ai/ai-thread-orchestration-contract.ts
  • src/features/workspaces/ai/ai-thread-orchestration.ts
  • src/features/workspaces/ai/ai-thread-orchestration.worker.test.ts
  • src/features/workspaces/ai/ai-thread-posthog-recorder.ts
  • src/features/workspaces/ai/ai-thread-runtime.ts
  • src/features/workspaces/ai/ai-thread-tool.test.ts
  • src/features/workspaces/ai/ai-thread-tool.ts
  • src/features/workspaces/ai/ai-thread.ts
  • src/features/workspaces/ai/ai-tool-outcome.ts
  • src/features/workspaces/ai/ai-tool-outcome.worker.test.ts
  • src/features/workspaces/ai/ai-tool-presentation.ts
  • src/features/workspaces/ai/ai-tool-registry.test.ts
  • src/features/workspaces/ai/ai-tool-registry.ts
  • src/features/workspaces/ai/code-run-tools.ts
  • src/features/workspaces/ai/research-tools.ts
  • src/features/workspaces/ai/time-tools.ts
  • src/features/workspaces/ai/web-tools.ts
  • src/features/workspaces/ai/workspace-tools.ts
  • src/features/workspaces/components/ai-chat/AiChatToolActivityRow.tsx
  • src/features/workspaces/components/ai-chat/ai-chat-codemode-activity.ts
  • src/features/workspaces/components/ai-chat/ai-chat-display-state.test.ts
  • src/features/workspaces/components/ai-chat/ai-chat-display-state.ts
  • src/integrations/firecrawl/research.ts
  • src/integrations/firecrawl/search.ts
💤 Files with no reviewable changes (1)
  • src/features/workspaces/ai/ai-tool-presentation.ts

Comment on lines +89 to +100
const output =
("output" in part ? part.output : undefined) ??
("result" in part ? part.result : undefined);
if (output === undefined) {
return part;
}

return {
...part,
output: stringifyForCompaction(output),
};
}),

Copy link
Copy Markdown

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

Ensure both result and output properties are stringified to prevent [object Object] rendering.

For standard AI SDK tool-result parts, this logic adds a new stringified output property but leaves the existing result property as an unstringified object. If the downstream agents summarizer formats tool-result parts by reading their native result field, it will encounter the original object and still render [object Object].

To fully adapt the results without relying on how agents extracts the value, overwrite result in place alongside output.

🐛 Proposed fix
-			const output =
-				("output" in part ? part.output : undefined) ??
-				("result" in part ? part.result : undefined);
-			if (output === undefined) {
-				return part;
-			}
-
-			return {
-				...part,
-				output: stringifyForCompaction(output),
-			};
+			const output =
+				("output" in part ? part.output : undefined) ??
+				("result" in part ? part.result : undefined);
+			if (output === undefined) {
+				return part;
+			}
+
+			const stringified = stringifyForCompaction(output);
+			const newPart = { ...part, output: stringified } as any;
+			if ("result" in part) {
+				newPart.result = stringified;
+			}
+			return newPart;
📝 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 output =
("output" in part ? part.output : undefined) ??
("result" in part ? part.result : undefined);
if (output === undefined) {
return part;
}
return {
...part,
output: stringifyForCompaction(output),
};
}),
const output =
("output" in part ? part.output : undefined) ??
("result" in part ? part.result : undefined);
if (output === undefined) {
return part;
}
const stringified = stringifyForCompaction(output);
const newPart = { ...part, output: stringified } as any;
if ("result" in part) {
newPart.result = stringified;
}
return newPart;
}),
🤖 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 `@src/features/workspaces/ai/ai-compaction.ts` around lines 89 - 100, Update
the mapping logic in ai-compaction to stringify both native result and output
fields on tool-result parts. Preserve the existing value selection and
unchanged-part behavior, but when a value exists, overwrite result alongside
output with stringifyForCompaction so downstream summarization cannot render
objects as [object Object].

Comment on lines +233 to +251
function getOrchestrationCallOutcome(
toolName: string,
state: z.output<typeof orchestrationCallStateSchema>,
result: unknown,
): AIToolOutcome {
if (state === "applied") {
return getAIToolOutputOutcome(toolName, result);
}

if (state === "pending") {
return { failureCodes: ["approval_pending"], failedCount: 0, status: "partial" };
}

return {
failureCodes: [state === "reverted" ? "codemode_tool_reverted" : "codemode_tool_error"],
failedCount: 1,
status: "error",
};
}

Copy link
Copy Markdown

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:

git ls-files src/features/workspaces/ai | sed -n '1,200p'

Repository: ThinkEx-OSS/thinkex

Length of output: 2565


🏁 Script executed:

ast-grep outline src/features/workspaces/ai/ai-thread-orchestration-contract.ts --view expanded
printf '\n--- ai-tool-outcome ---\n'
ast-grep outline src/features/workspaces/ai/ai-tool-outcome.ts --view expanded
printf '\n--- orchestration worker test ---\n'
ast-grep outline src/features/workspaces/ai/ai-thread-orchestration.worker.test.ts --view expanded
printf '\n--- tool outcome worker test ---\n'
ast-grep outline src/features/workspaces/ai/ai-tool-outcome.worker.test.ts --view expanded

Repository: ThinkEx-OSS/thinkex

Length of output: 2098


🏁 Script executed:

sed -n '180,265p' src/features/workspaces/ai/ai-thread-orchestration-contract.ts
printf '\n--- tool outcome ---\n'
sed -n '1,120p' src/features/workspaces/ai/ai-tool-outcome.ts
printf '\n--- search tests for executing/paused ---\n'
rg -n '"executing"|"paused"|executing|paused' src/features/workspaces/ai/*.test.ts

Repository: ThinkEx-OSS/thinkex

Length of output: 5042


🏁 Script executed:

sed -n '1,260p' src/features/workspaces/ai/ai-thread-orchestration.ts
printf '\n--- telemetry format ---\n'
sed -n '1,260p' src/features/workspaces/ai/ai-thread-telemetry-format.ts
printf '\n--- search for executing in ai feature ---\n'
rg -n '\bexecuting\b|orchestrationCallOutcome|getOrchestrationCallOutcome|getOrchestrationCallStatus|normalizeAIThreadOrchestrationOutput' src/features/workspaces/ai

Repository: ThinkEx-OSS/thinkex

Length of output: 11386


🏁 Script executed:

sed -n '1,240p' src/features/workspaces/ai/ai-thread-orchestration.worker.test.ts
printf '\n--- orchestration contract top ---\n'
sed -n '1,120p' src/features/workspaces/ai/ai-thread-orchestration-contract.ts

Repository: ThinkEx-OSS/thinkex

Length of output: 8587


🏁 Script executed:

sed -n '120,260p' src/features/workspaces/ai/ai-thread-orchestration-contract.ts

Repository: ThinkEx-OSS/thinkex

Length of output: 3692


🏁 Script executed:

rg -n '"running"|"approval_pending"|status: "paused"|status: "completed"|status: "error"' src/features/workspaces/ai/*.test.ts
printf '\n--- orchestration worker test around all cases ---\n'
sed -n '1,260p' src/features/workspaces/ai/ai-thread-orchestration.worker.test.ts

Repository: ThinkEx-OSS/thinkex

Length of output: 7031


Handle executing as a non-error state. getOrchestrationCallStatus already marks it running, but getOrchestrationCallOutcome still returns status: "error", which rolls into aggregateAIToolOutcomes and marks the whole orchestration failed. Add a non-error branch for executing and a regression test for the executing/paused paths.

🤖 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 `@src/features/workspaces/ai/ai-thread-orchestration-contract.ts` around lines
233 - 251, The getOrchestrationCallOutcome function currently treats executing
as an error. Add an executing branch that returns the non-error running outcome
expected by aggregateAIToolOutcomes, and add regression coverage for both
executing and paused orchestration paths while preserving existing applied,
pending, reverted, and error behavior.

Comment on lines +240 to 251
function addAIThreadToolEntries(entries: AIThreadToolEntry[], tools: ToolSet) {
for (const [name, tool] of Object.entries(tools)) {
if (!tool) {
continue;
}

entries.push(entry);
}

function addAIThreadToolEntries(
entries: AIThreadToolEntry[],
tools: ToolSet,
descriptors: AIThreadToolDescriptor[],
) {
for (const descriptor of descriptors) {
addAIThreadToolEntry(entries, {
...descriptor,
tool: tools[descriptor.name],
entries.push({
...requireAiToolDefinition(name).model,
name,
tool,
});
}

Copy link
Copy Markdown

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

Unregistered tool name crashes the whole turn, with no safety net.

requireAiToolDefinition(name) throws for any tool key not present in AI_TOOL_REGISTRY. Since addAIThreadToolEntries runs over every key returned by createSandboxTools/createAIThreadCodeRunTools/createAIThreadWebTools/createAIThreadResearchTools/createAIThreadTimeTools/createAIThreadWorkspaceTools on every beforeTurn, a single new/renamed first-party tool that isn't added to AI_TOOL_REGISTRY will throw during catalog construction and break turn setup for every thread that reaches this code path — not just callers of that one tool. There's currently no test enforcing that the registry stays in sync with the actual tool factories (ai-tool-registry.test.ts only checks two hand-picked entries).

Consider adding a coverage test (e.g. in ai-tool-registry.test.ts) that calls each create*Tools(...) factory with minimal/mock inputs and asserts requireAiToolDefinition doesn't throw for any resulting key, so a missing registration is caught in CI rather than at request time.

🤖 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 `@src/features/workspaces/ai/ai-thread-runtime.ts` around lines 240 - 251, Add
registry coverage in ai-tool-registry.test.ts that invokes each tool factory
used by addAIThreadToolEntries—createSandboxTools, createAIThreadCodeRunTools,
createAIThreadWebTools, createAIThreadResearchTools, createAIThreadTimeTools,
and createAIThreadWorkspaceTools—with minimal or mocked inputs, then verifies
requireAiToolDefinition does not throw for every returned tool key. Keep the
test focused on detecting missing AI_TOOL_REGISTRY entries during CI.

@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.

0 issues found across 10 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

Re-trigger cubic

@urjitc
urjitc merged commit 0d7e78e into main Jul 19, 2026
11 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Dev Board Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant