Skip to content

feat: built frontend and achieved arcitecture 100% 🎉#6

Merged
yb175 merged 6 commits into
mainfrom
client-app
Jun 25, 2026
Merged

feat: built frontend and achieved arcitecture 100% 🎉#6
yb175 merged 6 commits into
mainfrom
client-app

Conversation

@yb175

@yb175 yb175 commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary by cubic

Shipped a complete admin web console and finished the agent/policy architecture with parallel tool execution, symlink‑aware path sandboxing, and a puppeteer MCP plugin. Fixed the parallel resume path to re-check policies per tool and added clearer assistant DENY messages.

  • New Features

    • Web console in apps/web (Chat, Policies with sandbox_path, Approvals, Logs) using Next.js App Router, tailwindcss, and @reduxjs/toolkit (redirects //chat).
    • Agent runtime: supports single tool_call and parallel tool_calls with input schema validation; deterministic MOCK_LLM mode and model fallback.
    • Policy engine: symlink‑aware path sandbox rule; single‑approval orchestration for parallel batches; policies API exposes/accepts sandbox_path.
    • MCP: registered puppeteer via @modelcontextprotocol/server-puppeteer; manifests and .mcp.json updated.
    • Tooling: added apps/api/verify.js end‑to‑end verification script.
  • Bug Fixes

    • Parallel resume now re-evaluates PolicyEngine per tool before execution and deletes the approval atomically; aligns behavior with single-tool path.
    • Prevented approval replay on parallel deletes (Prisma P2025) by returning DENY.
    • Fixed polling races in Chat/Logs/Approvals and stopped forwarding stale approvalId.
    • Do not leak internal errors from /agent/run; assistant replies include explicit deny reasons on resume.

Written for commit 46d2f7f. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR ships a complete Next.js admin web console (Chat, Policies, Approvals, Logs) and finishes the agent/policy architecture with parallel tool execution, symlink-aware path sandboxing (pathRule.ts), and a puppeteer MCP plugin. Several previously reported issues are addressed: Redux side-effects are moved to middleware, the isRunningRef concurrent-polling guard is added, approval replay on parallel deletes returns DENY, and per-tool policy re-evaluation is added on parallel batch resume.

  • Agent runtime: loop.ts and decision.ts now handle both single tool_call and parallel tool_calls; policy is re-evaluated on resume for both paths, and the approval record is deleted before returning ALLOW to prevent replay.
  • Path sandbox rule: pathRule.ts adds symlink-aware sandbox enforcement with heuristic path-key detection, resolving the sandbox root to its real path to defeat symlink escapes.
  • Web console: Full Redux-backed UI with approval polling, isRunningRef concurrency guard, and history forwarding; store/index.ts moves all localStorage writes to middleware with the serializable check re-enabled.

Confidence Score: 4/5

Mostly safe to merge; one approval record cleanup path in decision.ts needs to be closed before this ships to a shared environment

The core approval-replay protections (parallel delete guard, per-tool policy re-evaluation on resume) are solid, but the single-tool path in decision.ts has a gap: when a tool's policy is changed to ALLOW while an APPROVED approval record exists, the record is never deleted. Any caller who holds that approvalId can re-execute the stored tool call with its original arguments on every subsequent run, effectively bypassing the policy change. Everything else — the path sandbox rule, the Redux middleware refactor, the concurrency guard in the chat page, and the input sanitization on the API endpoint — looks correct.

apps/api/src/policy/decision.ts — the ALLOW short-circuit at line 346 needs to consume the approval record when an approvalId was supplied by the caller

Important Files Changed

Filename Overview
apps/api/src/policy/decision.ts Core approval/decision engine expanded with parallel tool call support; contains a stale-approval replay issue when policy changes from APPROVAL to ALLOW
apps/api/src/agent/loop.ts Agent execution loop rewritten to support single and parallel tool calls with proper approval pre-fetch, iteration guard, and token accounting
apps/api/src/policy/rules/pathRule.ts New symlink-aware path sandbox rule with heuristic key detection; edge cases for traversal, absolute paths, and symlinks are all handled
apps/web/app/chat/page.tsx Full chat page with Redux state, approval polling, and concurrent-call guard (isRunningRef); tool name regex truncates hyphenated names
apps/web/store/index.ts Redux store with localStorage persistence moved to middleware, serializable check re-enabled — addresses prior review concerns
apps/web/store/chatSlice.ts Pure reducers with no side effects; all localStorage access removed from reducer bodies — addresses prior review concern
apps/web/services/policies.ts Policy service layer missing sandbox_path in Policy interface and CRUD functions despite the backend surfacing that field
apps/api/src/policy/engine.ts Policy engine updated to call the new pathRule sandbox check as step 2; pre-fetches policy record once to avoid TOCTOU
apps/api/src/agent/llm.ts Added MOCK_LLM mode for deterministic E2E testing and Grok fallback when Gemini key is absent
apps/api/src/index.ts Added input validation (history array, approvalId format) and internal error sanitization to the /agent/run endpoint

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as Web Console
    participant API as Agent API
    participant AgentLoop as loop.ts
    participant Decide as decide.ts
    participant Policy as PolicyEngine
    participant DB as Database

    UI->>API: "POST /agent/run {message}"
    API->>AgentLoop: runAgent(message, convId)
    AgentLoop->>AgentLoop: nextStep LLM tool_call
    AgentLoop->>Decide: "decide({tool_name, args})"
    Decide->>Policy: PolicyEngine(context)
    Policy-->>Decide: requiresApproval true
    Decide->>DB: approval.create PENDING
    Decide-->>AgentLoop: PENDING + approvalId
    AgentLoop-->>API: status PENDING, approvalId
    API-->>UI: status PENDING, approvalId
    Note over UI,DB: Admin approves via Approvals page
    UI->>API: PATCH /approvals/:id/approve
    API->>DB: approval.update APPROVED
    UI->>API: "POST /agent/run {approvalId}"
    API->>AgentLoop: runAgent(null, convId, approvalId)
    AgentLoop->>DB: approval.findUnique APPROVED
    AgentLoop->>AgentLoop: reconstruct step from approval.arguments
    AgentLoop->>Decide: "decide({tool_name, args, approvalId})"
    Decide->>Policy: PolicyEngine re-evaluated
    Policy-->>Decide: requiresApproval true
    Decide->>DB: approval.delete prevents replay
    Decide-->>AgentLoop: ALLOW
    AgentLoop->>AgentLoop: mcpExecutor.execute tool
    AgentLoop->>AgentLoop: nextStep final_answer
    AgentLoop-->>API: status SUCCESS
    API-->>UI: status SUCCESS, answer
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as Web Console
    participant API as Agent API
    participant AgentLoop as loop.ts
    participant Decide as decide.ts
    participant Policy as PolicyEngine
    participant DB as Database

    UI->>API: "POST /agent/run {message}"
    API->>AgentLoop: runAgent(message, convId)
    AgentLoop->>AgentLoop: nextStep LLM tool_call
    AgentLoop->>Decide: "decide({tool_name, args})"
    Decide->>Policy: PolicyEngine(context)
    Policy-->>Decide: requiresApproval true
    Decide->>DB: approval.create PENDING
    Decide-->>AgentLoop: PENDING + approvalId
    AgentLoop-->>API: status PENDING, approvalId
    API-->>UI: status PENDING, approvalId
    Note over UI,DB: Admin approves via Approvals page
    UI->>API: PATCH /approvals/:id/approve
    API->>DB: approval.update APPROVED
    UI->>API: "POST /agent/run {approvalId}"
    API->>AgentLoop: runAgent(null, convId, approvalId)
    AgentLoop->>DB: approval.findUnique APPROVED
    AgentLoop->>AgentLoop: reconstruct step from approval.arguments
    AgentLoop->>Decide: "decide({tool_name, args, approvalId})"
    Decide->>Policy: PolicyEngine re-evaluated
    Policy-->>Decide: requiresApproval true
    Decide->>DB: approval.delete prevents replay
    Decide-->>AgentLoop: ALLOW
    AgentLoop->>AgentLoop: mcpExecutor.execute tool
    AgentLoop->>AgentLoop: nextStep final_answer
    AgentLoop-->>API: status SUCCESS
    API-->>UI: status SUCCESS, answer
Loading

Reviews (5): Last reviewed commit: "fix: the parallal run problem" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Comment thread apps/web/app/chat/page.tsx
Comment thread apps/web/store/index.ts Outdated
Comment thread apps/web/store/chatSlice.ts Outdated
Comment thread apps/web/services/approvals.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

40 issues found across 45 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="apps/web/README.md">

<violation number="1" location="apps/web/README.md:28">
P2: Route structure docs state an automatic redirect from index to `/chat`, but the current page implementation does not redirect.</violation>
</file>

<file name="apps/api/src/policy/router.ts">

<violation number="1" location="apps/api/src/policy/router.ts:254">
P2: `GET /approvals` over-exposes approval payloads and returns an unbounded result set. Limit fields and row count to avoid leaking tool arguments and large responses.</violation>
</file>

<file name="apps/web/store/chatSlice.ts">

<violation number="1" location="apps/web/store/chatSlice.ts:87">
P2: `pendingApprovalStatus` is hydrated without validating allowed enum values. Validate persisted status and fallback to `null` for unknown values.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread apps/api/verify.js Outdated
Comment thread apps/api/README.md Outdated
Comment thread apps/api/src/policy/decision.ts Outdated
Comment thread .mcp.json Outdated
Comment thread apps/api/package.json Outdated
Comment thread apps/api/src/policy/rules/pathRule.ts
Comment thread apps/web/components/ApprovalTable.tsx Outdated
Comment thread apps/web/components/ChatWindow.tsx Outdated
Comment thread apps/web/README.md Outdated
Comment thread apps/web/README.md Outdated
Comment thread apps/web/app/chat/page.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

17 issues found across 29 files (changes from recent commits).

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="apps/web/README.md">

<violation number="1" location="apps/web/README.md:28">
P2: Route structure docs state an automatic redirect from index to `/chat`, but the current page implementation does not redirect.</violation>
</file>

<file name="apps/api/src/policy/router.ts">

<violation number="1" location="apps/api/src/policy/router.ts:254">
P2: `GET /approvals` over-exposes approval payloads and returns an unbounded result set. Limit fields and row count to avoid leaking tool arguments and large responses.</violation>
</file>

<file name="apps/web/store/chatSlice.ts">

<violation number="1" location="apps/web/store/chatSlice.ts:87">
P2: `pendingApprovalStatus` is hydrated without validating allowed enum values. Validate persisted status and fallback to `null` for unknown values.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread apps/api/verify.js
Comment thread apps/api/src/policy/decision.ts Outdated
Comment thread apps/api/src/policy/rules/pathRule.ts Outdated
Comment thread apps/api/src/policy/router.ts
Comment thread apps/api/verify.js Outdated
Comment thread apps/web/services/policies.ts Outdated
Comment thread apps/web/store/chatSlice.ts
Comment thread apps/api/README.md Outdated
Comment thread apps/web/app/chat/page.tsx Outdated
Comment thread apps/web/components/ApprovalTable.tsx

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

9 issues found across 21 files (changes from recent commits).

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="apps/web/README.md">

<violation number="1" location="apps/web/README.md:28">
P2: Route structure docs state an automatic redirect from index to `/chat`, but the current page implementation does not redirect.</violation>
</file>

<file name="apps/web/store/chatSlice.ts">

<violation number="1" location="apps/web/store/chatSlice.ts:87">
P2: `pendingApprovalStatus` is hydrated without validating allowed enum values. Validate persisted status and fallback to `null` for unknown values.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/api/src/policy/decision.ts
Comment thread apps/api/src/policy/rules/pathRule.ts Outdated
Comment thread apps/api/verify.js
Comment thread apps/web/components/PolicyTable.tsx Outdated
Comment thread apps/api/src/policy/router.ts
Comment thread apps/api/src/agent/llm.ts Outdated
Comment thread apps/api/src/agent/llm.ts Outdated
Comment thread apps/api/src/policy/rules/pathRule.ts Outdated
Comment thread apps/api/src/policy/router.ts Outdated
Comment thread apps/api/src/policy/decision.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 8 files (changes from recent commits).

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="apps/web/README.md">

<violation number="1" location="apps/web/README.md:28">
P2: Route structure docs state an automatic redirect from index to `/chat`, but the current page implementation does not redirect.</violation>
</file>

<file name="apps/web/store/chatSlice.ts">

<violation number="1" location="apps/web/store/chatSlice.ts:87">
P2: `pendingApprovalStatus` is hydrated without validating allowed enum values. Validate persisted status and fallback to `null` for unknown values.</violation>
</file>

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

Fix all with cubic | Re-trigger cubic

Comment thread apps/api/src/policy/router.ts Outdated
Comment thread apps/api/src/policy/router.ts Outdated
Comment thread apps/api/src/policy/decision.ts
Comment thread apps/api/src/policy/decision.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

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="apps/web/README.md">

<violation number="1" location="apps/web/README.md:28">
P2: Route structure docs state an automatic redirect from index to `/chat`, but the current page implementation does not redirect.</violation>
</file>

<file name="apps/web/store/chatSlice.ts">

<violation number="1" location="apps/web/store/chatSlice.ts:87">
P2: `pendingApprovalStatus` is hydrated without validating allowed enum values. Validate persisted status and fallback to `null` for unknown values.</violation>
</file>

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

Fix all with cubic | Re-trigger cubic

Comment thread apps/api/src/policy/decision.ts
@yb175 yb175 merged commit bfad174 into main Jun 25, 2026
3 checks passed
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