Skip to content

feat(ai): typed custom events + sandbox file-diff hooks#892

Open
AlemTuzlak wants to merge 18 commits into
mainfrom
feat/typed-custom-events
Open

feat(ai): typed custom events + sandbox file-diff hooks#892
AlemTuzlak wants to merge 18 commits into
mainfrom
feat/typed-custom-events

Conversation

@AlemTuzlak

@AlemTuzlak AlemTuzlak commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What

Makes every framework-emitted CUSTOM event type-constrained on the chat stream, and gives sandbox file hooks lazy git-backed diffs.

  • Typed custom events. Every framework CUSTOM event (sandbox.file, file.changed, ${adapter}.session-id, code_mode:*, skill:registered, plus the already-typed structured-output/tool-interaction/ui events) is now a discriminated interface unified as KnownCustomEvent. The chat stream is retyped ChatStream = AsyncIterable<Exclude<StreamChunk, CustomEvent> | KnownCustomEvent>, so a plain if (chunk.type === 'CUSTOM' && chunk.name === '<literal>') narrows chunk.value with no helper and no cast. No guard utilities by design.
  • Sandbox file-hook accessors. onFile* hooks receive a SandboxFileHookEvent with lazy, git-backed before() / after() / diff() (computed on call — path-only hooks pay nothing). The serialized sandbox.file chunk stays path-only.
  • Opt-in live diffs. defineSandbox({ fileEvents: { diff: true } }) emits a per-file sandbox.file.diff { path, diff } chunk.

Tests / quality

  • Unit tests for the accessors, wire-projection (no accessor leak), and event typing; a real-handle integration test (localProcessSandbox + real git) that caught a genuine bug — diff() was passing the virtual /workspace path to git diff instead of relativizing, silently yielding '' on every local-process sandbox.
  • Command-injection hardening: all git exec args are shell-quoted.
  • kiira green (774/774 snippets); @tanstack/ai and @tanstack/ai-sandbox typechecks green. Docs + ag-ui-protocol/middleware skills updated.
  • The full 50-project test:pr sweep was not completed locally (monorepo-wide fan-out); CI runs it.

Notes for reviewers

  • E2E: the testing/e2e/ harness has no sandbox wiring (aimock LLM-only), so browser-level sandbox E2E is structurally infeasible here — covered by the real-handle integration test instead. True sandbox E2E infra is a suggested follow-up.
  • docs/adapters/openrouter.md: its cost snippet fence was tagged ```typescript ignore, matching the existing structured-outputs/streaming.md precedent — a kiira-env resolution quirk with @ag-ui/core zod types through Exclude<> unions, not a real type regression (package tsc is green; real consumers narrow fine).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added strongly-typed CUSTOM streaming via ChatStream, including a KnownCustomEvent taxonomy.
    • Introduced opt-in sandbox file diffs (sandbox.file.diff) with lazy hook accessors (before, after, diff).
  • Bug Fixes
    • Improved exec-poll watcher path discovery/normalization to map detected changes to the correct workspace paths.
  • Documentation
    • Added a Custom Events Reference and expanded streaming and sandbox hook/diff guides with typed narrowing examples.
  • Tests
    • Added/updated integration and type tests covering file-diff emission and new event typing.

AlemTuzlak added 14 commits July 3, 2026 14:01
…e.diff stream chunks

Adds a real-handle integration test (localProcessSandbox + a real git repo)
proving withSandbox's fileEvents:{diff:true} emits both sandbox.file and
sandbox.file.diff CUSTOM stream chunks, read back via literal-name narrowing
on the public KnownCustomEvent type without a cast.

Driving a real handle surfaced a genuine bug in buildFileHookEvent's diff()
accessor: it passed the virtual sandbox path straight to `git diff -- <path>`
instead of relativizing it like before() already does, so git resolved the
leading `/` against the filesystem root and failed with "fatal: Invalid
path" on every local-process sandbox. Fixed in file-diff.ts, with updated
unit coverage in file-diff.test.ts.

Lives in ai-sandbox-local-process (not ai-sandbox) so it can use a real
provider via public exports only, avoiding a reverse workspace
devDependency.
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

1 package(s) bumped directly, 0 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/ai-openrouter 0.15.6 → 0.15.7 Changeset

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds typed CUSTOM event unions, sandbox file-hook accessors with optional diff emission, chat/runtime wiring for sandbox.file.diff, exec-poll watcher normalization, and updated docs/tests across the sandbox and AI packages.

Changes

Typed CUSTOM events and sandbox file-diff hooks

Layer / File(s) Summary
ChatStream and KnownCustomEvent contracts
packages/ai/src/types.ts, packages/ai/src/activities/chat/index.ts, packages/ai/tests/chat-result-types.test.ts, packages/ai/tests/known-custom-events.test-d.ts
Adds new CustomEvent interfaces, KnownCustomEvent, and ChatStream, then updates streaming return typing and type-level tests to use ChatStream.
File-diff computation utilities
packages/ai-sandbox/src/file-diff.ts, packages/ai-sandbox/tests/file-diff.test.ts
Adds path-relative git helpers, buildFileHookEvent, resolveFileEvents, and tests for before(), after(), and diff().
Sandbox hooks and watcher wiring
packages/ai-sandbox/src/sandbox.ts, packages/ai-sandbox/src/middleware.ts, packages/ai-sandbox/tests/with-sandbox-hooks.test.ts, packages/ai-sandbox-local-process/package.json, packages/ai-sandbox-local-process/tests/sandbox-file-diff.integration.test.ts
Expands fileEvents to `boolean
AI package SandboxFileHookEvent propagation
packages/ai/src/activities/chat/middleware/*.ts, packages/ai/src/activities/chat/index.ts, packages/ai/src/index.ts, packages/ai/tests/sandbox-*.test.ts
Propagates SandboxFileHookEvent through chat middleware, runtime typing, exports, and tests, and emits sandbox.file / sandbox.file.diff chunks.
Exec-poll watch path normalization
packages/ai-sandbox/src/watch.ts, packages/ai-sandbox/tests/watch.test.ts
Changes poll-watch find handling to use cwd-relative output and normalize parsed paths back under the configured root.
Documentation for typed CUSTOM events and sandbox observability
docs/protocol/custom-events.md, docs/config.json, docs/sandbox/events.md, docs/sandbox/observability.md, docs/chat/streaming.md, docs/adapters/openrouter.md, packages/ai/skills/ai-core/*/SKILL.md
Adds the custom events reference page, updates navigation metadata, and revises sandbox/streaming/skill docs for typed CUSTOM events and hook accessors.

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

Sequence Diagram(s)

sequenceDiagram
  participant Watcher
  participant withSandbox
  participant buildFileHookEvent
  participant Hooks
  participant SandboxRuntime
  participant ChatEngine

  Watcher->>withSandbox: raw file change event
  withSandbox->>buildFileHookEvent: buildFileHookEvent(handle, watchRoot, baseSha, event)
  buildFileHookEvent-->>withSandbox: SandboxFileHookEvent
  withSandbox->>Hooks: dispatchDefinitionHooks(enriched)
  withSandbox->>SandboxRuntime: emit(enriched)
  SandboxRuntime->>ChatEngine: enqueue sandbox.file chunk
  alt fileEvents.diff enabled
    withSandbox->>buildFileHookEvent: enriched.diff()
    buildFileHookEvent-->>withSandbox: unified diff string
    withSandbox->>SandboxRuntime: emitFileDiff({ path, diff })
    SandboxRuntime->>ChatEngine: enqueue sandbox.file.diff chunk
  end
Loading

Possibly related PRs

  • TanStack/ai#587: Overlaps on typed CUSTOM-stream event interfaces and ChatStream/StreamChunk narrowing behavior.

Suggested reviewers: tombeckenham

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the changes well, but it misses the template's required Checklist and Release Impact sections. Add the Checklist and Release Impact sections, including local test confirmation and whether this PR needs a changeset or is docs/dev-only.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: typed custom events and sandbox file-diff hooks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/typed-custom-events

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.

@nx-cloud

nx-cloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 40f4295

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 8m View ↗
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 2m 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-03 17:50:42 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@892

@tanstack/ai-acp

npm i https://pkg.pr.new/@tanstack/ai-acp@892

@tanstack/ai-angular

npm i https://pkg.pr.new/@tanstack/ai-angular@892

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@892

@tanstack/ai-bedrock

npm i https://pkg.pr.new/@tanstack/ai-bedrock@892

@tanstack/ai-claude-code

npm i https://pkg.pr.new/@tanstack/ai-claude-code@892

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@892

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@892

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@892

@tanstack/ai-codex

npm i https://pkg.pr.new/@tanstack/ai-codex@892

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@892

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@892

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@892

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@892

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@892

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@892

@tanstack/ai-grok-build

npm i https://pkg.pr.new/@tanstack/ai-grok-build@892

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@892

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@892

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@892

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@892

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@892

@tanstack/ai-mistral

npm i https://pkg.pr.new/@tanstack/ai-mistral@892

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@892

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@892

@tanstack/ai-opencode

npm i https://pkg.pr.new/@tanstack/ai-opencode@892

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@892

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@892

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@892

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@892

@tanstack/ai-sandbox

npm i https://pkg.pr.new/@tanstack/ai-sandbox@892

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-sandbox-cloudflare@892

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/@tanstack/ai-sandbox-daytona@892

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/@tanstack/ai-sandbox-docker@892

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/@tanstack/ai-sandbox-local-process@892

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/@tanstack/ai-sandbox-sprites@892

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/@tanstack/ai-sandbox-vercel@892

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@892

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@892

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@892

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@892

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@892

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@892

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@892

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@892

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@892

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@892

commit: 0e20cf3

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

🧹 Nitpick comments (3)
packages/ai-sandbox/src/middleware.ts (1)

127-137: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

git rev-parse HEAD runs even when fileEvents: false.

baseSha is computed unconditionally before resolveFileEvents(definition.fileEvents) is checked, so sandboxes that explicitly disable file watching still pay for a subprocess exec on every session setup.

♻️ Suggested reorder
       const watchRoot = definition.workspace?.root ?? DEFAULT_WORKSPACE_ROOT
-      let baseSha = ''
-      try {
-        const shaRes = await handle.process.exec('git rev-parse HEAD', {
-          cwd: watchRoot,
-        })
-        if (shaRes.exitCode === 0) baseSha = shaRes.stdout.trim()
-      } catch {
-        // non-git workspace / exec rejects → baseSha stays '' (accessors fall back)
-      }
+      const fe = resolveFileEvents(definition.fileEvents)
+      let baseSha = ''
+      if (fe.enabled) {
+        try {
+          const shaRes = await handle.process.exec('git rev-parse HEAD', {
+            cwd: watchRoot,
+          })
+          if (shaRes.exitCode === 0) baseSha = shaRes.stdout.trim()
+        } catch {
+          // non-git workspace / exec rejects → baseSha stays '' (accessors fall back)
+        }
+      }

(remove the later duplicate const fe = resolveFileEvents(...) at line 165)

Also applies to: 165-165

🤖 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/ai-sandbox/src/middleware.ts` around lines 127 - 137, The
`middleware.ts` setup is always running `git rev-parse HEAD` to compute
`baseSha` even when `definition.fileEvents` disables file watching. Move the
`resolveFileEvents(definition.fileEvents)` check ahead of the
`handle.process.exec('git rev-parse HEAD', ...)` call in the middleware logic,
and only compute `baseSha` when file events are enabled; then remove the later
duplicate `const fe = resolveFileEvents(...)` so the `watchRoot`/`baseSha` work
is skipped entirely for `fileEvents: false`.
packages/ai-sandbox/tests/file-diff.test.ts (1)

64-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding coverage for diff() on change/delete events with an empty baseSha.

Current tests only exercise diff()'s empty-base path via a create event (Line 69). Given the synthesizeAddPatch semantics concern raised in file-diff.ts, tests for change/delete + empty baseSha would help pin down the intended behavior.

🤖 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/ai-sandbox/tests/file-diff.test.ts` around lines 64 - 73, Add test
coverage in file-diff.test.ts for FileHookEvent.diff() when baseSha is empty and
the event type is change and delete, not just create. Use buildFileHookEvent and
the diff() method to verify the intended synthesized patch behavior for
empty-base handling, matching the semantics of synthesizeAddPatch in
file-diff.ts.
packages/ai/src/types.ts (1)

1384-1449: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

New CUSTOM event interfaces look correct and well-scoped.

Discriminated by literal name, consistent with how KnownCustomEvent/ChatStream narrow downstream. One nit: FileChangedEvent.value (Line 1401) and SandboxFileDiffEvent.value (Line 1395) share the identical { path: string; diff: string } shape — consider extracting a shared FileDiffPayload type to avoid drift if one evolves independently.

🤖 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/ai/src/types.ts` around lines 1384 - 1449, Extract the shared `{
path: string; diff: string }` payload used by SandboxFileDiffEvent.value and
FileChangedEvent.value into a reusable type, such as a shared file-diff payload
near the existing Sandbox and Harness event interfaces in types.ts. Then update
both SandboxFileDiffEvent and FileChangedEvent to reference that shared type so
the shape stays aligned and future changes only need to be made in one place.
🤖 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 `@docs/sandbox/observability.md`:
- Around line 141-144: The fallback description is too broad and conflates the
accessor behavior for deleted files, new files, and non-git workspaces. Update
the matrix in the observability docs so it matches the interface semantics: use
the accessor names before(), after(), and diff() explicitly, and show that
deleted files still have before(), new files still have after(), and non-git
workspaces only synthesize diff() from after(). Apply the same wording
correction in SKILL.md so both docs describe the same accessor-specific
fallbacks consistently.

In `@packages/ai-sandbox/src/file-diff.ts`:
- Around line 56-75: The diff() path for non-git workspaces currently treats
every event like a new file, which misrepresents delete and change events.
Update the baseSha === '' branch in diff() to branch on event.type: reserve
synthesizeAddPatch(event.path, await after()) for create only, return an
appropriate removed/empty patch for delete, and avoid using add-patch semantics
for change. Use event.type, after(), and synthesizeAddPatch as the key symbols
to locate the logic.

In `@packages/ai-sandbox/src/middleware.ts`:
- Around line 165-186: Track the pending enriched.diff() work in middleware.ts
so it is awaited before teardown; right now watchWorkspace's onEvent fires the
diff promise without tracking it, and onFinish/onAbort/onError only wait for
watcher.stop(). Update the middleware flow around resolveFileEvents,
buildFileHookEvent, and the fe.diff branch to collect in-flight diff promises
and await them before the session closes so runtime?.emitFileDiff(...) is not
dropped.

In `@packages/ai-sandbox/tests/file-diff.test.ts`:
- Line 1: The import in the file-diff test is out of alphabetical order and
triggers the sort-imports lint rule. Update the import declaration in the test
file so the named imports from vitest are sorted alphabetically, keeping the
existing describe, it, and expect usage intact.

In `@packages/ai/src/activities/chat/index.ts`:
- Around line 714-724: The fire-and-forget call in the sandbox file hook emitter
can create an unhandled rejection because
`this.middlewareRunner.runSandboxFile(...)` is invoked with `void` and no error
handling. Update the `emit` callback in `Chat`’s constructor logic to either
route this work through the existing `ctx.defer()` / `Promise.allSettled`
pattern used elsewhere, or attach a `.catch()` that logs the failure via
`this.logger` so hook errors are handled safely. Use the existing `emit`
callback and `middlewareRunner.runSandboxFile` as the target points for the fix.
- Around line 725-738: The sandbox file diff event can be queued after the final
streamed model chunk and never drained, so make sure `sandbox.file.diff` is
flushed before the run completes. Update the streaming flow in
`streamModelResponse()` and the event handlers around
`emitFileDiff`/`sandboxFileQueue` in `chat/index.ts` so any pending queue
entries are emitted after the `git diff` promise settles, not only while model
chunks are streaming. Also verify the `compose.ts` `.then()` path still routes
its diff output through the same flushing mechanism.

In `@packages/ai/src/activities/chat/middleware/types.ts`:
- Around line 28-35: The SandboxFileHookEvent interface uses shorthand method
signatures for before, after, and diff, which violates the
`@typescript-eslint/method-signature-style` rule in this codebase. Update those
members in SandboxFileHookEvent to function-type properties while keeping the
same Promise<string> return types and semantics, so the interface matches the
project’s preferred style.

In `@packages/ai/tests/known-custom-events.test-d.ts`:
- Around line 5-24: The `KnownCustomEvent` assertions in
`known-custom-events.test-d.ts` are already narrowed to `type: 'CUSTOM'`, so the
`ev.type === 'CUSTOM'` checks are redundant and trigger
`@typescript-eslint/no-unnecessary-condition`. Update the test cases around
`KnownCustomEvent`, `isSessionIdEvent`, and the related `expectTypeOf`
assertions to remove those always-true guards, or alternatively add a targeted
lint disable with a brief explanation if you need to preserve a real-world
narrowing shape.

In `@packages/ai/tests/sandbox-runtime-emit.test.ts`:
- Around line 23-28: The `queue.push` payload in `sandbox-runtime-emit.test.ts`
already matches `StreamChunk`, so the `as StreamChunk` assertion is redundant.
Remove the unnecessary cast from the object literal passed to `queue.push` and
keep the existing `EventType.CUSTOM`/`sandbox.file` structure unchanged.

---

Nitpick comments:
In `@packages/ai-sandbox/src/middleware.ts`:
- Around line 127-137: The `middleware.ts` setup is always running `git
rev-parse HEAD` to compute `baseSha` even when `definition.fileEvents` disables
file watching. Move the `resolveFileEvents(definition.fileEvents)` check ahead
of the `handle.process.exec('git rev-parse HEAD', ...)` call in the middleware
logic, and only compute `baseSha` when file events are enabled; then remove the
later duplicate `const fe = resolveFileEvents(...)` so the `watchRoot`/`baseSha`
work is skipped entirely for `fileEvents: false`.

In `@packages/ai-sandbox/tests/file-diff.test.ts`:
- Around line 64-73: Add test coverage in file-diff.test.ts for
FileHookEvent.diff() when baseSha is empty and the event type is change and
delete, not just create. Use buildFileHookEvent and the diff() method to verify
the intended synthesized patch behavior for empty-base handling, matching the
semantics of synthesizeAddPatch in file-diff.ts.

In `@packages/ai/src/types.ts`:
- Around line 1384-1449: Extract the shared `{ path: string; diff: string }`
payload used by SandboxFileDiffEvent.value and FileChangedEvent.value into a
reusable type, such as a shared file-diff payload near the existing Sandbox and
Harness event interfaces in types.ts. Then update both SandboxFileDiffEvent and
FileChangedEvent to reference that shared type so the shape stays aligned and
future changes only need to be made in one place.
🪄 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: faeff44d-f795-4537-99ea-9911307aa6f5

📥 Commits

Reviewing files that changed from the base of the PR and between f80a279 and fefca25.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • docs/adapters/openrouter.md
  • docs/chat/streaming.md
  • docs/config.json
  • docs/protocol/custom-events.md
  • docs/sandbox/events.md
  • docs/sandbox/observability.md
  • packages/ai-sandbox-local-process/package.json
  • packages/ai-sandbox-local-process/tests/sandbox-file-diff.integration.test.ts
  • packages/ai-sandbox/src/file-diff.ts
  • packages/ai-sandbox/src/middleware.ts
  • packages/ai-sandbox/src/sandbox.ts
  • packages/ai-sandbox/tests/file-diff.test.ts
  • packages/ai-sandbox/tests/with-sandbox-hooks.test.ts
  • packages/ai/skills/ai-core/ag-ui-protocol/SKILL.md
  • packages/ai/skills/ai-core/middleware/SKILL.md
  • packages/ai/src/activities/chat/index.ts
  • packages/ai/src/activities/chat/middleware/compose.ts
  • packages/ai/src/activities/chat/middleware/index.ts
  • packages/ai/src/activities/chat/middleware/sandbox-runtime.ts
  • packages/ai/src/activities/chat/middleware/types.ts
  • packages/ai/src/index.ts
  • packages/ai/src/types.ts
  • packages/ai/tests/chat-result-types.test.ts
  • packages/ai/tests/known-custom-events.test-d.ts
  • packages/ai/tests/sandbox-file-dispatch.test.ts
  • packages/ai/tests/sandbox-runtime-emit.test.ts

Comment thread docs/sandbox/observability.md Outdated
Comment on lines +56 to +75
const diff = async (): Promise<string> => {
if (baseSha === '') return synthesizeAddPatch(event.path, await after())
// Pathspec must be relative to `root` (like `before()` above) — a bare
// leading `/` (e.g. the virtual `/workspace/x.ts`) is resolved by git
// against the filesystem root, not the repo root, and fails with
// "fatal: Invalid path" whenever the real repo root differs from
// `/workspace` (e.g. every local-process sandbox).
const rel = relTo(root, event.path)
try {
const res = await handle.process.exec(
`git diff ${q(baseSha)} -- ${q(rel)}`,
{
cwd: root,
},
)
return res.exitCode === 0 ? res.stdout : ''
} catch {
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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

diff() mishandles change/delete events in non-git workspaces.

When baseSha === '', diff() unconditionally calls synthesizeAddPatch(event.path, await after()) — but that helper is documented as being for "a brand-new file" only (Line 17). For a delete event, after() returns '', so this produces a zero-line "add" patch with a --- /dev/null header, which misrepresents a deletion as a no-op creation. For a change event, it presents the entire current file as newly added rather than reflecting that only some content changed. Only the create path is currently covered by tests.

Consider branching on event.type here (e.g., emit an empty/removed-file patch for delete, and reserve synthesizeAddPatch for create only).

🐛 Proposed fix
   const diff = async (): Promise<string> => {
-    if (baseSha === '') return synthesizeAddPatch(event.path, await after())
+    if (baseSha === '') {
+      if (event.type === 'delete') return ''
+      return synthesizeAddPatch(event.path, await after())
+    }
📝 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 diff = async (): Promise<string> => {
if (baseSha === '') return synthesizeAddPatch(event.path, await after())
// Pathspec must be relative to `root` (like `before()` above) — a bare
// leading `/` (e.g. the virtual `/workspace/x.ts`) is resolved by git
// against the filesystem root, not the repo root, and fails with
// "fatal: Invalid path" whenever the real repo root differs from
// `/workspace` (e.g. every local-process sandbox).
const rel = relTo(root, event.path)
try {
const res = await handle.process.exec(
`git diff ${q(baseSha)} -- ${q(rel)}`,
{
cwd: root,
},
)
return res.exitCode === 0 ? res.stdout : ''
} catch {
return ''
}
}
const diff = async (): Promise<string> => {
if (baseSha === '') {
if (event.type === 'delete') return ''
return synthesizeAddPatch(event.path, await after())
}
// Pathspec must be relative to `root` (like `before()` above) — a bare
// leading `/` (e.g. the virtual `/workspace/x.ts`) is resolved by git
// against the filesystem root, not the repo root, and fails with
// "fatal: Invalid path" whenever the real repo root differs from
// `/workspace` (e.g. every local-process sandbox).
const rel = relTo(root, event.path)
try {
const res = await handle.process.exec(
`git diff ${q(baseSha)} -- ${q(rel)}`,
{
cwd: root,
},
)
return res.exitCode === 0 ? res.stdout : ''
} catch {
return ''
}
}
🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 64-69: Avoid command injection
Context: handle.process.exec(
git diff ${q(baseSha)} -- ${q(rel)},
{
cwd: root,
},
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(command-injection-typescript)

🪛 OpenGrep (1.23.0)

[ERROR] 65-70: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

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

In `@packages/ai-sandbox/src/file-diff.ts` around lines 56 - 75, The diff() path
for non-git workspaces currently treats every event like a new file, which
misrepresents delete and change events. Update the baseSha === '' branch in
diff() to branch on event.type: reserve synthesizeAddPatch(event.path, await
after()) for create only, return an appropriate removed/empty patch for delete,
and avoid using add-patch semantics for change. Use event.type, after(), and
synthesizeAddPatch as the key symbols to locate the logic.

Comment thread packages/ai-sandbox/src/middleware.ts
Comment thread packages/ai-sandbox/tests/file-diff.test.ts Outdated
Comment thread packages/ai/src/activities/chat/index.ts Outdated
Comment on lines 725 to 738
this.sandboxFileQueue.push(
this.createCustomEventChunk('sandbox.file', { ...event }),
this.createCustomEventChunk('sandbox.file', {
type: event.type,
path: event.path,
timestamp: event.timestamp,
}),
)
},
emitFileDiff: (value: { path: string; diff: string }) => {
this.sandboxFileQueue.push(
this.createCustomEventChunk('sandbox.file.diff', value),
)
},
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail
printf 'Repo root: '; pwd
git ls-files 'packages/ai/src/activities/chat/index.ts' 'packages/ai/src/activities/chat/middleware.ts' | cat
printf '\n-- drainSandboxFileQueue refs --\n'
rg -n "drainSandboxFileQueue|sandboxFileQueue|emitFileDiff|sandbox\.file\.diff|sandbox\.file" packages/ai/src/activities/chat -S
printf '\n-- index.ts outline --\n'
ast-grep outline packages/ai/src/activities/chat/index.ts --view expanded
printf '\n-- middleware.ts outline --\n'
ast-grep outline packages/ai/src/activities/chat/middleware.ts --view expanded

Repository: TanStack/ai

Length of output: 10017


🏁 Script executed:

set -euo pipefail
sed -n '1120,1185p' packages/ai/src/activities/chat/index.ts | cat -n
printf '\n----\n'
sed -n '680,770p' packages/ai/src/activities/chat/index.ts | cat -n
printf '\n---- middleware ----\n'
sed -n '1,260p' packages/ai/src/activities/chat/middleware.ts | cat -n

Repository: TanStack/ai

Length of output: 7169


🏁 Script executed:

set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('packages/ai/src/activities/chat/index.ts')
text = p.read_text()
for needle in ['drainSandboxFileQueue(', 'streamModelResponse(', 'processToolCalls(', 'checkForPendingToolCalls(', 'pipeThroughMiddleware(']:
    print(f'## {needle}')
    for i, line in enumerate(text.splitlines(), 1):
        if needle in line:
            print(i, line.strip())
PY

Repository: TanStack/ai

Length of output: 1597


🏁 Script executed:

set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('packages/ai/src/activities/chat/index.ts').read_text().splitlines()
targets = ['deferredPromises', 'await Promise.all', 'pipeThroughMiddleware(', 'runStructuredFinalization(', 'harvestCombinedStructuredOutput(', 'finalStructuredOutput', 'middlewareRunner.runSandboxFile', 'runSandboxFile(']
for needle in targets:
    print(f'## {needle}')
    for i, line in enumerate(text, 1):
        if needle in line:
            print(f'{i}: {line.strip()}')
    print()
PY

Repository: TanStack/ai

Length of output: 4020


🏁 Script executed:

set -euo pipefail
sed -n '780,920p' packages/ai/src/activities/chat/index.ts | cat -n
printf '\n----\n'
sed -n '2528,2608p' packages/ai/src/activities/chat/index.ts | cat -n
printf '\n---- sandbox runtime ----\n'
git ls-files 'packages/ai/src/activities/chat/middleware/*' | cat
printf '\n----\n'
sed -n '1,220p' packages/ai/src/activities/chat/middleware/sandbox-runtime.ts | cat -n

Repository: TanStack/ai

Length of output: 11270


🏁 Script executed:

set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('packages/ai/src/activities/chat/index.ts').read_text().splitlines()
for start, end in [(1000, 1170), (1280, 1615), (1960, 2365), (2540, 2608)]:
    print(f'==== {start}-{end} ====')
    for i in range(start, min(end, len(text)) + 1):
        print(f'{i}: {text[i-1]}')
    print()
PY

Repository: TanStack/ai

Length of output: 41178


🏁 Script executed:

set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('packages/ai/src/activities/chat/index.ts').read_text().splitlines()
for needle in ['deferredPromises', 'Promise.allSettled', 'Promise.all', 'await this.deferredPromises', 'void this.middlewareRunner.runSandboxFile', 'runSandboxFile(', 'emitFileDiff:']:
    print(f'## {needle}')
    for i, line in enumerate(text, 1):
        if needle in line:
            print(f'{i}: {line.strip()}')
    print()
PY

Repository: TanStack/ai

Length of output: 851


🏁 Script executed:

set -euo pipefail
sed -n '778,930p' packages/ai/src/activities/chat/index.ts | cat -n

Repository: TanStack/ai

Length of output: 7149


🏁 Script executed:

set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('packages/ai/src/activities/chat/index.ts').read_text().splitlines()
for start, end in [(778, 930), (930, 1015), (2544, 2578)]:
    print(f'==== {start}-{end} ====')
    for i in range(start, min(end, len(text)) + 1):
        print(f'{i}: {text[i-1]}')
    print()
PY

Repository: TanStack/ai

Length of output: 11459


🏁 Script executed:

set -euo pipefail
sed -n '650,690p' packages/ai/src/activities/chat/index.ts | cat -n

Repository: TanStack/ai

Length of output: 1893


🏁 Script executed:

set -euo pipefail
rg -n "emitFileDiff\(" packages/ai/src -S

Repository: TanStack/ai

Length of output: 149


🏁 Script executed:

set -euo pipefail
sed -n '70,150p' packages/ai/src/activities/chat/middleware/compose.ts | cat -n

Repository: TanStack/ai

Length of output: 3605


🏁 Script executed:

set -euo pipefail
printf 'Files:\n'
git ls-files 'packages/ai/src/activities/chat/**' | cat

printf '\n-- relevant symbols --\n'
rg -n "sandbox\.file\.diff|emitFileDiff|composeAgentFileTreeWithDiff|git diff|Diff mode|diff mode" packages/ai/src/activities/chat -S

printf '\n-- compose files outline --\n'
fd -a 'compose.ts' packages/ai/src/activities/chat || true
fd -a 'middleware.ts' packages/ai/src/activities/chat || true
fd -a 'sandbox-runtime.ts' packages/ai/src/activities/chat || true

Repository: TanStack/ai

Length of output: 2368


Flush sandbox.file.diff before the run finishes. packages/ai/src/activities/chat/middleware/compose.ts emits diffs from a .then() after git diff, but packages/ai/src/activities/chat/index.ts:725-738 only drains sandboxFileQueue from streamModelResponse(). If that promise settles after the last streamed chunk, the queued sandbox.file.diff can be left behind and never reach the consumer.

🤖 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/ai/src/activities/chat/index.ts` around lines 725 - 738, The sandbox
file diff event can be queued after the final streamed model chunk and never
drained, so make sure `sandbox.file.diff` is flushed before the run completes.
Update the streaming flow in `streamModelResponse()` and the event handlers
around `emitFileDiff`/`sandboxFileQueue` in `chat/index.ts` so any pending queue
entries are emitted after the `git diff` promise settles, not only while model
chunks are streaming. Also verify the `compose.ts` `.then()` path still routes
its diff output through the same flushing mechanism.

Comment thread packages/ai/src/activities/chat/middleware/types.ts
Comment thread packages/ai/tests/known-custom-events.test-d.ts
Comment thread packages/ai/tests/sandbox-runtime-emit.test.ts Outdated
@harshlocham

harshlocham commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

@AlemTuzlak
I reproduced this locally and verified the lint failure.

The SandboxFileHookEvent interface currently uses shorthand method signatures:

before(): Promise<string>
after(): Promise<string>
diff(): Promise<string>

This violates the repository's ESLint configuration (@typescript-eslint/method-signature-style: ["error", "property"]).

Changing them to function properties resolves the lint error:

before: () => Promise<string>
after: () => Promise<string>
diff: () => Promise<string>

I verified the fix by running:

pnpm eslint packages/ai/src/activities/chat/middleware/types.ts
pnpm test:pr

After applying the change, the full pnpm test:pr suite completed successfully.

@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

🤖 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/ai/skills/ai-core/middleware/SKILL.md`:
- Around line 451-456: Update the non-git workspace fallback description in
SKILL.md to match the actual behavior of the accessors and diff logic. In the
section describing `before()`, `after()`, and `diff()`, state that `after()`
still returns current on-disk content, only `before()` resolves to `''` in a
non-git workspace, and `diff()` synthesizes an add-patch from `after()` unless
the event is `delete`, in which case it returns `''`.
🪄 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: 588a806e-bd01-4e5a-944c-ddc99900e844

📥 Commits

Reviewing files that changed from the base of the PR and between 40f4295 and 0e20cf3.

📒 Files selected for processing (8)
  • docs/sandbox/observability.md
  • packages/ai-sandbox/src/file-diff.ts
  • packages/ai-sandbox/src/middleware.ts
  • packages/ai-sandbox/tests/file-diff.test.ts
  • packages/ai/skills/ai-core/middleware/SKILL.md
  • packages/ai/src/activities/chat/index.ts
  • packages/ai/tests/known-custom-events.test-d.ts
  • packages/ai/tests/sandbox-runtime-emit.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • docs/sandbox/observability.md
  • packages/ai-sandbox/tests/file-diff.test.ts
  • packages/ai-sandbox/src/middleware.ts
  • packages/ai/src/activities/chat/index.ts
  • packages/ai/tests/known-custom-events.test-d.ts
  • packages/ai-sandbox/src/file-diff.ts
  • packages/ai/tests/sandbox-runtime-emit.test.ts

Comment on lines +451 to +456
accessors throw: a deleted file resolves `after()` to `''` (it still has
`before()`); a new file resolves `before()` to `''` (it still has `after()`);
a non-git workspace resolves **both** `before()` and `after()` to `''` and
makes `diff()` fall back to a synthesized add-patch built from `after()` —
except for a `delete` event in a non-git workspace, where there's nothing to
synthesize and `diff()` resolves to `''`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant documentation and inspect the surrounding section.
git ls-files packages/ai/skills/ai-core/middleware/SKILL.md
wc -l packages/ai/skills/ai-core/middleware/SKILL.md
sed -n '380,490p' packages/ai/skills/ai-core/middleware/SKILL.md

# Find nearby references to after(), before(), diff(), git, and non-git fallback.
rg -n "after\(\)|before\(\)|diff\(\)|non-git|git workspace|synthesized add-patch|delete event" packages/ai/skills/ai-core/middleware/SKILL.md

Repository: TanStack/ai

Length of output: 5484


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the document structure for the section around the cited lines.
ast-grep outline packages/ai/skills/ai-core/middleware/SKILL.md --view expanded

Repository: TanStack/ai

Length of output: 210


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search the repo for the implementation or other docs that define the semantics.
rg -n "current on-disk content|resolves `after\(\)`|resolves `before\(\)`|synthesized add-patch|non-git workspace" packages -g '*.md' -g '*.ts' -g '*.js'

Repository: TanStack/ai

Length of output: 26859


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the exact section around the cited lines and nearby contract text.
sed -n '420,470p' packages/ai/skills/ai-core/middleware/SKILL.md
sed -n '470,540p' packages/ai/skills/ai-core/middleware/SKILL.md

Repository: TanStack/ai

Length of output: 4255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the authoritative type docs and the sandbox diff implementation.
sed -n '1,120p' packages/ai/src/activities/chat/middleware/types.ts
sed -n '1,120p' packages/ai-sandbox/src/file-diff.ts
sed -n '120,220p' packages/ai-sandbox/src/middleware.ts

Repository: TanStack/ai

Length of output: 10702


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate tests that cover non-git and delete-event behavior.
rg -n "non-git|delete event|synthesized add-patch|before\\(|after\\(|diff\\(\\)" packages/ai-sandbox/tests packages/ai/tests packages/ai/src -g '*.test.ts' -g '*.ts'

Repository: TanStack/ai

Length of output: 2175


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant sandbox tests covering file diff semantics.
sed -n '1,180p' packages/ai-sandbox/tests/file-diff.test.ts
sed -n '1,220p' packages/ai-sandbox/tests/with-sandbox-hooks.test.ts

Repository: TanStack/ai

Length of output: 11141


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect specific source files for the runtime semantics.
cat -n packages/ai/src/activities/chat/middleware/types.ts | sed -n '1,120p'
echo '---'
cat -n packages/ai-sandbox/src/file-diff.ts | sed -n '1,220p'
echo '---'
cat -n packages/ai-sandbox/src/middleware.ts | sed -n '110,180p'

Repository: TanStack/ai

Length of output: 11949


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the implementation that drives the accessors.
cat -n packages/ai-sandbox/src/middleware.ts | sed -n '120,220p'
echo '---'
cat -n packages/ai-sandbox/src/file-diff.ts | sed -n '1,220p'

Repository: TanStack/ai

Length of output: 8003


Fix the non-git fallback description. after() still reads current on-disk content; only before() becomes '' in a non-git workspace. diff() then falls back to the synthesized add-patch from after(), with delete returning ''.

Suggested wording tweak
- a non-git workspace resolves **both** `before()` and `after()` to `''` and makes `diff()` fall back to a synthesized add-patch built from `after()`
+ a non-git workspace resolves `before()` to `''`, still reads `after()` from disk, and makes `diff()` fall back to a synthesized add-patch built from `after()`
📝 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
accessors throw: a deleted file resolves `after()` to `''` (it still has
`before()`); a new file resolves `before()` to `''` (it still has `after()`);
a non-git workspace resolves **both** `before()` and `after()` to `''` and
makes `diff()` fall back to a synthesized add-patch built from `after()`
except for a `delete` event in a non-git workspace, where there's nothing to
synthesize and `diff()` resolves to `''`.
accessors throw: a deleted file resolves `after()` to `''` (it still has
`before()`); a new file resolves `before()` to `''` (it still has `after()`);
a non-git workspace resolves `before()` to `''`, still reads `after()` from disk,
and makes `diff()` fall back to a synthesized add-patch built from `after()`
except for a `delete` event in a non-git workspace, where there's nothing to
synthesize and `diff()` resolves to `''`.
🧰 Tools
🪛 SkillSpector (2.3.7)

[warning] 54: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 58: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 58: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 59: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 60: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 61: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 62: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 63: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 64: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 65: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 66: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 67: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 83: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 85: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 86: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 87: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 88: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 89: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 236: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))


[warning] 241: [MP2] Context Window Stuffing: Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Remediation: Implement context-window management that detects and rejects padding or stuffing attempts. Prioritize system instructions over user-injected content.

(Memory Poisoning (MP2))

🤖 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/ai/skills/ai-core/middleware/SKILL.md` around lines 451 - 456,
Update the non-git workspace fallback description in SKILL.md to match the
actual behavior of the accessors and diff logic. In the section describing
`before()`, `after()`, and `diff()`, state that `after()` still returns current
on-disk content, only `before()` resolves to `''` in a non-git workspace, and
`diff()` synthesizes an add-patch from `after()` unless the event is `delete`,
in which case it returns `''`.

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.

2 participants