Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.

FEA-1407: Add sandbox scoping and transcript sanitization to agent session sync - #245

Merged
thadeusb merged 6 commits into
mainfrom
feat/fea-1407
May 28, 2026
Merged

FEA-1407: Add sandbox scoping and transcript sanitization to agent session sync#245
thadeusb merged 6 commits into
mainfrom
feat/fea-1407

Conversation

@thadeusb

@thadeusb thadeusb commented May 27, 2026

Copy link
Copy Markdown
Contributor

Feature

FEA-1407 — Agent Session Sync: Sandbox Scoping & Transcript Sanitization

WRK-177 — HIGH priority

Summary

  • Sandbox scoping at ingestion — sessions with cwd outside the configured sandbox are rejected at both ingestion chokepoints (hooks route + import-history) and never enter the local DB
  • Defense-in-depth sync filter — sessions already in the DB from before this change (or with null cwd) are filtered out at sync time before cloud upload
  • Transcript sanitization — recursively strips user-authored content (prompt, content, stdout, stderr) from event data before cloud sync while preserving all structural metadata (tool_name, tool_input, filePath, type, interrupted, isImage, durationMs, numFiles, etc.). Also strips agents[].task (user prompt text). Session metadata, summaries, tool args, attribution, and token usage are all preserved.
  • Sidecar restart on sandbox change — updating the sandbox in settings restarts the sidecar with the new SANDBOX_BASE_DIRECTORY env var

Architecture

flowchart TD
    subgraph "Layer 1: Ingestion Filter"
        H["Claude Code Hook Event"] -->|"POST /event"| HC{"data.cwd in sandbox?"}
        HC -->|No| HR["Return 200 OK — skip"]
        HC -->|Yes / no cwd| HE["enqueueHookEvent → DB"]
        I["File Watcher / Cold Import"] -->|"importSession"| IC{"session.cwd in sandbox?"}
        IC -->|No| IR["Return skipped"]
        IC -->|Yes| IE["Insert/Update → DB"]
    end
    subgraph "Layer 2: Sync Service Filter"
        HE --> DB["dashboard.db"]
        IE --> DB
        DB -->|"syncOnce"| SF{"cwd in current sandbox?"}
        SF -->|No| SD["Skip — dequeue silently"]
        SF -->|Yes| SN["Include in batch"]
    end
    subgraph "Layer 3: Sanitization"
        SN --> SAN["Recursive strip:\ncontent, stdout, stderr,\nprompt, agent.task"]
        SAN --> CLOUD["Cloud Upload —\nstructure + metadata only"]
    end
Loading

Sanitization: What's Stripped vs Kept

Data Kept Stripped
Session metadata (counts, permission mode, turn metrics)
Session name, status, model, harness, timestamps
Agent name, type, status, timestamps
Agent task (user prompt)
Event type, tool name, summary, timestamps
Event data structural keys (tool_name, tool_input, filePath, type, durationMs, numFiles, interrupted, isImage, etc.)
Event data prompt (user message text)
Event data content (file contents, at any depth)
Event data stdout / stderr (command output)
Token usage, cost estimates
Attribution (repo, branch, artifact IDs)

Example — a Read tool PostToolUse event before/after:

// Before
{ "tool_name": "Read", "tool_response": { "type": "text", "file": { "filePath": "/app/.env", "content": "DB_PASSWORD=secret" } } }
// After
{ "tool_name": "Read", "tool_response": { "type": "text", "file": { "filePath": "/app/.env" } } }

Plan

PLN-739 — plan challenge passed (Sonnet PASS, Opus PASS)

Key Decisions

  • isSessionInSandbox() uses path.resolve() not fs.realpathSync() — matches existing pragmatic behavior, avoids filesystem access in generated code
  • Empty sandbox (setup incomplete) blocks all session sync — intentional fail-closed behavior matching the gateway's deny-all-without-sandbox posture
  • Sanitization is recursive — strips content, stdout, stderr, prompt at any nesting depth inside data, preserving all structural/metadata keys
  • Build-script patches share a single IS_SESSION_IN_SANDBOX_CJS constant to avoid duplication

Decision Table

Verified at .closedloop-ai/decision-tables/fea-1407.md — all delta checklist items implemented, all acceptance criteria verified.

Review Summary

3 parallel reviewers (correctness/Opus, security/Sonnet, conventions/Opus). Findings:

  • Correctness blocker (existing tests break without sandbox callback) — fixed
  • Security advisory (session.name may contain user prompts) — evaluated, kept (auto-generated, not user content)
  • Conventions (duplicated helper in build script) — fixed, extracted shared constant
  • Reviewer feedback (mikeangstadt): added debug logging for sandbox-filtered sessions

Downstream Updates

None — no sibling features linked.

Feature Flags

None — security fix restoring intended sandbox enforcement.

Database/Migration Safety

No schema/migration changes.

Test Plan

  • isSessionInSandbox returns true for cwd inside sandbox
  • isSessionInSandbox returns false for cwd outside sandbox
  • isSessionInSandbox returns false for null/empty cwd
  • isSessionInSandbox returns false for null/empty sandbox
  • isSessionInSandbox with sandbox "/" allows all paths
  • isSessionInSandbox rejects prefix-match without path separator
  • isSessionInSandbox handles trailing slashes
  • sanitizeSessionForSync strips agent.task
  • sanitizeSessionForSync strips content/stdout/stderr recursively inside tool_response
  • sanitizeSessionForSync preserves structural metadata (tool_name, filePath, type, interrupted, isImage)
  • sanitizeSessionForSync preserves data without stripped keys
  • sanitizeSessionForSync does not mutate original session
  • Static: SANDBOX_BASE_DIRECTORY in sidecar spawn env
  • Static: sandbox patch functions in build script

…ssion sync

- Pass SANDBOX_BASE_DIRECTORY env var to agent-monitor sidecar at spawn
- Restart sidecar on sandbox directory change in settings
- Patch hooks route (build script) to reject hook events outside sandbox
- Patch import-history (build script) to skip sessions outside sandbox
- Add defense-in-depth sandbox filter in sync service before cloud upload
- Strip user content (event summary/data, agent task/metadata, session
  name/metadata) from sessions before cloud sync — preserves structural
  fields (eventType, toolName, timestamps, tokenUsage, attribution)
- Add hard-gate assertions for both sandbox patches in build script
- Add getSandboxBaseDirectory to AgentSessionSyncServiceOptions

Testing:
- 12 new unit tests for isSessionInSandbox edge cases and sanitization
- 3 new static assertions for sidecar env var and build script patches
- Updated existing tests to provide sandbox directory (getSandboxBaseDirectory)
- Updated oversized-session tests to use tool_name padding (survives sanitization)

Risks:
- Empty sandbox (setup incomplete) now blocks all session sync — intentional
  fail-closed behavior matching the gateway's deny-all-without-sandbox posture
- Sessions already in DB from before this change are filtered at sync time
  (defense-in-depth) but not retroactively purged from local DB
@thadeusb
thadeusb requested a review from a team May 27, 2026 21:46

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

ℹ️ 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".

Comment thread apps/desktop/src/main/agent-session-sync-service.ts
Comment thread apps/desktop/scripts/build-agent-monitor.mjs
Comment thread apps/desktop/test/agent-session-sync-service.test.ts
Comment thread apps/desktop/scripts/build-agent-monitor.mjs
Comment thread apps/desktop/src/main/agent-session-sync-service.ts

@thadeusb thadeusb left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Clean layered design, the three filter points are consistent and the sanitizer is correct. Ship it.

Comment thread apps/desktop/scripts/build-agent-monitor.mjs
Comment thread apps/desktop/src/main/agent-session-sync-service.ts
Comment thread apps/desktop/test/agent-session-sync-service.test.ts
@thadeusb
thadeusb merged commit 103b323 into main May 28, 2026
5 checks passed
@thadeusb
thadeusb deleted the feat/fea-1407 branch May 28, 2026 20:46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants