Skip to content

feat(sessions): 支持事件引用已挂载文件 - #227

Open
pxguan wants to merge 13 commits into
mainfrom
codex/session-event-file-references
Open

feat(sessions): 支持事件引用已挂载文件#227
pxguan wants to merge 13 commits into
mainfrom
codex/session-event-file-references

Conversation

@pxguan

@pxguan pxguan commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

变更摘要

  • 扩展 user.message,支持在 document / image content block 中通过 source.type=file + file_id 引用已挂载的 Files API 文件。
  • Session 创建的 initial_events 与运行中的 events.send 使用同一套文件引用校验,并要求文件已通过 Session Resources API 挂载。
  • 公开 Session Event 始终保留用户原始消息和 file_id;只在 Code Session inbound 边界转换为 Claude Code @"/mnt/session/uploads/..." 路径引用。
  • active Session 实时投递与 activation 历史回放共用转换逻辑;转换后的 inbound 继续复用现有持久化、ACK、重试和重连链路。
  • 拒绝客户端本地绝对路径、未挂载或跨 Session 文件、非图片 MIME 的 image 引用;批量事件任一文件无效时整批回滚。

设计与数据边界

session_events.payload
  = 用户可见的原始消息和 file_id

code_session_inbound_events.payload
  = worker 专用消息,包含 @"/mnt/session/uploads/..."

不增加新的 Session Event payload 字段或 migration。事件列表、SSE、Webhook、审计和前端展示都不会读取或泄漏 worker 绝对路径。详细设计见 docs/design/be/session-event-file-references.md

测试

  • 本次暂存文件 pre-commit hook 全部通过。
  • 受影响 Go package 的 golangci-lint:0 issues。
  • 死代码、Go/TypeScript 重复代码、Go 复杂度和大文件检查通过。
  • 基于最新 main 的受影响 package 测试全部通过:internal/sessioneventfilesinternal/sessionsinternal/codesessionsinternal/dbinternal/sandboxmount
  • 独立数据库中新增的 Session 文件事件 API 测试全部通过:已挂载文件的 realtime/activation 转换、未挂载文件整批回滚、创建时 initial_events 文件引用。

兼容说明

  • PR 分支已重放到最新 main,不依赖 feat(agents): 支持自定义 MCP 服务器配置 #218
  • 本地共享数据库曾被其他未合并分支迁移;API 回归使用独立数据库,真实 E2E 完成后已恢复共享数据库约束并验证无空 ownership。

Summary by CodeRabbit

  • New Features
    • Added support for file references in session and deployment events.
    • Initial session events can reference files mounted in the session.
    • File references are converted to sandbox paths for code-session processing while public event data retains file identifiers.
    • Added validation for file ownership, mounting, supported image types, malformed references, and duplicate paths.
  • Bug Fixes
    • Event batches now fail atomically when a referenced file is invalid.
    • Files referenced by session events cannot be deleted.
    • Deployment runs reject initial events that reference unavailable files.
    • Realtime event delivery now uses validated mounted file references.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Session events now support Files API references. Public payloads retain file IDs, while validated mounted resources become Sandbox paths in worker payloads. Session creation, event submission, activation, realtime delivery, deployment runs, and resource deletion enforce the new binding rules.

Changes

Session event file references

Layer / File(s) Summary
Reference validation and worker payload rules
internal/sessioneventfiles/*, internal/sandboxmount/path.go, internal/sessions/event_files.go, internal/sessions/file_resources.go, internal/sessions/service_helpers.go
Validates file blocks, mounted bindings, image MIME types, and paths. Converts valid references into deduplicated, escaped Sandbox path mentions.
Transactional event persistence
internal/db/session_event_writes.go, internal/db/session_resource_mapper.*, internal/db/session_event_mapper.*, internal/db/sessions.go, internal/db/sessions_helpers.go, internal/db/sessions_mapper_test.go
Adds transaction-scoped event writes and binding queries. Initial events and outcome evaluations are persisted atomically. Referenced resources cannot be deleted.
Session and deployment event validation
internal/sessions/handler.go, internal/sessions/service.go, internal/deployments/handler.go, internal/deployments/resources.go, internal/deployments/handler_contract_test.go, tests/sessions_api_test.go, tests/deployments_api_test.go
Accepts and validates initial events during session creation and deployment runs. Event submission reloads and locks the session, validates bindings, and appends the batch transactionally.
Worker delivery and end-to-end verification
internal/codesessions/*, docs/design/be/session-event-file-references.md, docs/design/be/session-startup-message-delivery.md, tests/sessions_api_test.go
Activation and queued delivery convert persisted public events into worker inbound events with mounted paths. Tests cover public payloads, SSE, replay, deletion protection, deployment flows, and atomicity.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SessionsService
  participant SessionEventWriteTx
  participant CodeSession
  participant Sandbox

  Client->>SessionsService: Submit session event with file_id
  SessionsService->>SessionEventWriteTx: Lock session and load active bindings
  SessionEventWriteTx-->>SessionsService: Return file ID, path, and MIME type
  SessionsService->>SessionEventWriteTx: Validate and append public event
  SessionsService->>CodeSession: Queue public event with bindings
  CodeSession->>Sandbox: Convert file reference to mounted path
  CodeSession-->>Client: Deliver worker event through session delivery
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了本次变更的主要内容,即支持 Session 事件引用已挂载文件。
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/session-event-file-references

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.

@pxguan
pxguan force-pushed the codex/session-event-file-references branch from ca6b91b to 9e291f7 Compare August 10, 2026 13:39
@pxguan
pxguan changed the base branch from codex/issue-217-custom-mcp-server to main August 10, 2026 13:40
@pxguan
pxguan marked this pull request as ready for review August 10, 2026 14:02
@pxguan pxguan self-assigned this Aug 10, 2026
@pxguan

pxguan commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

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

ℹ️ 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 internal/codesessions/service.go Outdated
Comment thread internal/sessions/event_files.go

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

Important

本 PR 在 Code Session 的 activation/realtime 转换阶段重新解析文件引用并硬失败:一旦某条已被 API 接受的事件引用的文件未挂载或已被卸载,activation 会整体回滚导致 run 启动失败。而 deployment 创建的 run 写 initial_events 时完全绕过本 PR 新增的挂载校验,之前可运行的 deployment 可能因此直接失败。建议在 deployment run 路径补上同一套挂载校验,并统一 activation 与 realtime 对无法解析引用的处理策略。

Reviewed changes
本 PR 让 user.messagedocument / image content block 可通过 source.type=file + file_id 引用已挂载的 Files API 文件,并只在 Code Session inbound 边界转换为 Claude Code @"/mnt/session/uploads/..." 路径引用。

  • 新增 internal/sessioneventfiles 包:校验 file-source block、拒绝客户端本地路径、仅对 user.message 生效,并在 worker 边界把公开 block 替换为去重后的路径 mention。
  • Session 创建新增 initial_events 支持;events.send 移入持有 Session 行锁的 WithSessionEventWriteTx,挂载校验与事件落库同事务原子完成。
  • internal/codesessions 的 realtime 投递与 activation 回放共用 workerPayloadForPublicEvent 转换;公开边界保留原始 file_id,worker payload 存 /mnt/session/uploads/...
  • DB 新增 ListEventFileBindings 查询与 CreateSessionInput.InitialEvents,XML 仅按 UUID/workspace 关联、无外键。
  • 新增本地 E2E 脚本与 3 个 API 集成测试,覆盖 realtime/activation 转换、整批回滚、创建时 initial_events 引用。

⚠️ Deployment run 的 initial_events 绕过挂载校验,且 activation 对无法解析的引用硬失败

activation 与 realtime 在事件已被 API 接受(200、公开 session_events 已落库)后重新按当前 DB 挂载绑定解析文件引用,两条路径对“绑定消失”的处理不一致:

  • realtime(internal/codesessions/service.go:82-83):转换失败仅 continue,事件被静默丢弃,客户端已收到成功响应但该消息(含文本)永远不会投递给 worker;
  • activation(internal/codesessions/managed_agent_code_session.go:171-176):转换失败直接 return err,整个 WithManagedAgentActivationTx 回滚,后续 cleanup 终止 code session,run 的 agent 不会启动。

而 deployment 创建的 run 完全不经本 PR 新增的 ValidateMountedReferences / ValidatePublicEventinternal/deployments/handler.gorunRoutesessionEventsFromInitialEventsCreateManualDeploymentRuninternal/db/deployments.go:211 直接 insertSessionEventsTx),且 deployment 创建时 validateContentSourceinternal/deployments/handler.go:1602)允许 source.type=file 但不校验 file_id 是否属于 deployment.resources。因此在 initial_events 引用未挂载文件的 deployment,本 PR 之前会原样透传并成功启动,现在会直接失败(触发链已由独立核查确认)。

同样的硬失败还会出现在:事件已发送后、activation 前卸载了对应 file resource;以及 realtime 路径下发送事务提交到 queue 重新查询之间资源被并发删除(窗口极小)。

Technical details
# 已接受事件中的文件引用在转换期硬失败

## Affected sites
- internal/codesessions/managed_agent_code_session.go:171-176 — activation 转换失败 `return err`,整体回滚,code session 被 cleanup 终止
- internal/codesessions/service.go:80-83 — realtime 转换失败仅 `continue`,事件静默丢弃(API 已返回 200)
- internal/deployments/handler.go:566,632 + internal/db/deployments.go:211 — deployment run 的 initial_events 不经过 ValidateMountedReferences/ValidatePublicEvent 直接落库
- internal/deployments/handler.go:1602-1604 — deployment 创建允许 source.type=file 但不校验 file_id ∈ deployment.resources

## Required outcome
- deployment run 路径与 sessions API 创建路径应用同一套挂载校验;或在 deployment 创建/更新时拒绝引用未挂载文件的 initial_events
- activation 对无法解析的历史文件引用应有明确策略:跳过该引用而不是让整个 replay 失败(与 realtime 的 skip 行为对齐),或保证 send 时已校验的绑定在转换期仍有效
- realtime 路径不要静默丢弃已接受的事件,至少让失败可告警、可追踪

## Open questions for the human
- 是否应阻止卸载被事件引用过的资源?
- activation 失败后是否需要向用户暴露具体错误或提供重试?

ℹ️ Nitpicks

  • 新增的 3 个 API 测试只覆盖 sessions API 路径;deployment run 路径(initial_events + 文件引用)是本次发现的回归点但没有测试。
  • internal/sessioneventfiles/references_test.go:89 的第一条断言对 “file is not attached” 和 “image mime type mismatch” 两个 case 是恒真 no-op(ValidatePublicEvent 对这两类返回 nilerr != nil 恒为假),读起来像在断言实际未覆盖,建议改为直接比对 ValidatePublicEvent 的返回。

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash𝕏

Comment thread internal/codesessions/managed_agent_code_session.go Outdated

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

🧹 Nitpick comments (6)
tests/sessions_api_test.go (1)

3754-3756: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert that initial-event worker payloads exclude file_id.

The current assertion passes if the worker payload contains both the mounted path and the original file_id. Reject "file_id" here, as Lines 3646-3647 already do for activation replay of submitted events.

Proposed test change
- if len(inbound) != 2 || !bytes.Contains(inbound[1].Payload, []byte(`/mnt/session/uploads/initial.pdf`)) {
+ if len(inbound) != 2 ||
+   !bytes.Contains(inbound[1].Payload, []byte(`/mnt/session/uploads/initial.pdf`)) ||
+   bytes.Contains(inbound[1].Payload, []byte(`"file_id"`)) {
    t.Fatalf("initial inbound events = %#v, want mounted path", inbound)
  }
🤖 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 `@tests/sessions_api_test.go` around lines 3754 - 3756, Update the initial
inbound-event assertion in the session test to require the mounted path while
also rejecting any `"file_id"` field in the worker payload, matching the
existing activation-replay assertion near lines 3646-3647. Keep the event-count
check and failure diagnostics intact.
internal/sandboxmount/path.go (1)

72-88: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the shared path check to remove duplication with FileBackingPath.

Lines 75-85 repeat the filestorepath.Validate call and the control-character loop from FileBackingPath (Lines 54-61). Extract one helper and call it from both functions. This keeps the two functions inside the duplicate-code budget.

♻️ Proposed refactor
+func validateBackingPath(label, value string) error {
+	if err := filestorepath.Validate(value, false); err != nil {
+		return fmt.Errorf("%s %w", label, err)
+	}
+	for _, r := range value {
+		if unicode.IsControl(r) {
+			return fmt.Errorf("%s must not contain control characters", label)
+		}
+	}
+	return nil
+}
+
 func SandboxFilePath(backingPath string) (string, error) {
-	if err := filestorepath.Validate(backingPath, false); err != nil {
-		return "", fmt.Errorf("file backing path %w", err)
+	if err := validateBackingPath("file backing path", backingPath); err != nil {
+		return "", err
 	}
 	if !strings.HasPrefix(backingPath, FileSource+"/") {
 		return "", fmt.Errorf("file backing path must be under %q", FileSource)
 	}
-	for _, value := range backingPath {
-		if unicode.IsControl(value) {
-			return "", errors.New("file backing path must not contain control characters")
-		}
-	}
 	return SandboxUploadsMount + strings.TrimPrefix(backingPath, FileSource), nil
 }
🤖 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 `@internal/sandboxmount/path.go` around lines 72 - 88, Extract the shared
backing-path validation currently duplicated in SandboxFilePath and
FileBackingPath into a private helper, including filestorepath.Validate and the
control-character check. Call that helper from both functions, preserving their
existing path-prefix validation and error behavior.

Source: Coding guidelines

internal/sessioneventfiles/references.go (1)

63-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Return a nil payload when validation fails.

WorkerPayload returns raw together with a non-nil error at Line 66. A caller that logs the error and still forwards the first return value sends the unexpanded public payload, including file_id, to the worker. Every other failure path returns nil. Make this path consistent.

♻️ Proposed change
 	references, err := referencesFromEvent(eventType, raw)
-	if err != nil || len(references) == 0 {
-		return raw, err
+	if err != nil {
+		return nil, err
+	}
+	if len(references) == 0 {
+		return raw, nil
 	}
🤖 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 `@internal/sessioneventfiles/references.go` around lines 63 - 67, Update
WorkerPayload so the referencesFromEvent validation-error path returns a nil
payload alongside the error, while preserving the existing raw-payload return
when no references are found without an error.
internal/sessioneventfiles/references_test.go (3)

89-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the ValidatePublicEvent assertion non-vacuous.

The condition only fails when the error is non-nil and the message does not match. If ValidatePublicEvent returns nil, the check passes. The "client local path" case then proves nothing about the public boundary. Add an expectation for whether each case must fail at the public boundary.

💚 Proposed change
 	tests := []struct {
 		name     string
 		payload  string
 		bindings []Binding
+		public   bool
 		want     string
 	}{
 			raw := json.RawMessage(test.payload)
-			if err := ValidatePublicEvent("user.message", raw); err != nil && !strings.Contains(err.Error(), test.want) {
-				t.Fatalf("ValidatePublicEvent() error = %v, want containing %q", err, test.want)
+			publicErr := ValidatePublicEvent("user.message", raw)
+			if test.public && (publicErr == nil || !strings.Contains(publicErr.Error(), test.want)) {
+				t.Fatalf("ValidatePublicEvent() error = %v, want containing %q", publicErr, test.want)
+			}
+			if !test.public && publicErr != nil {
+				t.Fatalf("ValidatePublicEvent() error = %v, want nil", publicErr)
 			}

Set public: true for the "client local path" case and public: false for the two mount-dependent cases.

🤖 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 `@internal/sessioneventfiles/references_test.go` around lines 89 - 91, Make the
ValidatePublicEvent test assertion non-vacuous by adding the per-case
public-boundary expectation: set public to true for “client local path” and
false for both mount-dependent cases. Update the test logic to assert whether
ValidatePublicEvent("user.message", raw) returns an error according to that
expectation, while retaining the existing message-content check for expected
failures.

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

Order the failure test before the success tests.

TestReferenceValidationFailures is declared after TestWorkerPayloadInjectsDeduplicatedMountedPaths. Move the failure test above both success tests.

As per coding guidelines: "Order tests with failure scenarios before success scenarios".

Also applies to: 60-60, 100-100

🤖 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 `@internal/sessioneventfiles/references_test.go` at line 10, Reorder the test
declarations in the references test file so TestReferenceValidationFailures
appears before both success tests, including
TestWorkerPayloadInjectsDeduplicatedMountedPaths, while leaving the test
implementations unchanged.

Source: Coding guidelines


20-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add coverage for a file reference in a non-user.message event.

referencesFromEvent rejects file blocks when eventType != "user.message" (Lines 140-142 of internal/sessioneventfiles/references.go). No test exercises that branch. Add a case that passes system.message with a source.type=file block.

🤖 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 `@internal/sessioneventfiles/references_test.go` around lines 20 - 58, Extend
the references test coverage to call WorkerPayload with event type
system.message and a payload containing a source.type=file block, then assert
referencesFromEvent rejects or handles it according to the existing
non-user-message behavior. Keep the current user.message assertions unchanged
and ensure the new case exercises the eventType != "user.message" branch.
🤖 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/design/be/session-startup-message-delivery.md`:
- Around line 11-14: Update the design statement around session_events.payload
and Files API references to document both conversion boundaries: activation
replay and active realtime delivery via workerPayloadForPublicEvent. Clarify
that conversion produces Claude Code mount paths for inbound events while the
original session_events.payload remains unchanged.

In `@internal/codesessions/service.go`:
- Line 80: Update the event conversion flow around workerPayloadForPublicEvent
so any conversion error is returned through the existing retryable delivery path
before QueueRawPublicSessionEvents, rather than logged and skipped. Preserve
successful handling of valid events, and add a mixed-batch test covering one
valid event and one unresolved file reference to verify persistence, retry, and
delivery behavior.

In `@internal/db/session_resource_mapper.xml`:
- Around line 137-143: Update the ListEventFileBindings query to require an
active mount by adding the resource.expires_at predicate alongside the existing
resource filters. Extend the relevant mapper assertion in
sessions_mapper_test.go to verify this expiry condition is present.

In `@tests/sessions_api_test.go`:
- Around line 3559-3709: Reorder the test functions so
TestSessionEventMissingFileResourceRejectsWholeBatch appears before the
successful TestSessionEventFileReferencesUseMountedResources scenario. Do not
change either test’s implementation or assertions.

---

Nitpick comments:
In `@internal/sandboxmount/path.go`:
- Around line 72-88: Extract the shared backing-path validation currently
duplicated in SandboxFilePath and FileBackingPath into a private helper,
including filestorepath.Validate and the control-character check. Call that
helper from both functions, preserving their existing path-prefix validation and
error behavior.

In `@internal/sessioneventfiles/references_test.go`:
- Around line 89-91: Make the ValidatePublicEvent test assertion non-vacuous by
adding the per-case public-boundary expectation: set public to true for “client
local path” and false for both mount-dependent cases. Update the test logic to
assert whether ValidatePublicEvent("user.message", raw) returns an error
according to that expectation, while retaining the existing message-content
check for expected failures.
- Line 10: Reorder the test declarations in the references test file so
TestReferenceValidationFailures appears before both success tests, including
TestWorkerPayloadInjectsDeduplicatedMountedPaths, while leaving the test
implementations unchanged.
- Around line 20-58: Extend the references test coverage to call WorkerPayload
with event type system.message and a payload containing a source.type=file
block, then assert referencesFromEvent rejects or handles it according to the
existing non-user-message behavior. Keep the current user.message assertions
unchanged and ensure the new case exercises the eventType != "user.message"
branch.

In `@internal/sessioneventfiles/references.go`:
- Around line 63-67: Update WorkerPayload so the referencesFromEvent
validation-error path returns a nil payload alongside the error, while
preserving the existing raw-payload return when no references are found without
an error.

In `@tests/sessions_api_test.go`:
- Around line 3754-3756: Update the initial inbound-event assertion in the
session test to require the mounted path while also rejecting any `"file_id"`
field in the worker payload, matching the existing activation-replay assertion
near lines 3646-3647. Keep the event-count check and failure diagnostics intact.
🪄 Autofix

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 Plus

Run ID: d7f0993d-14f8-4b58-86a0-a0135258d7fd

📥 Commits

Reviewing files that changed from the base of the PR and between 25ddf6d and 9e291f7.

📒 Files selected for processing (21)
  • docs/design/be/session-event-file-references.md
  • docs/design/be/session-startup-message-delivery.md
  • internal/codesessions/managed_agent_code_session.go
  • internal/codesessions/mapper.go
  • internal/codesessions/service.go
  • internal/db/managed_agent_activation.go
  • internal/db/session_event_writes.go
  • internal/db/session_resource_mapper.go
  • internal/db/session_resource_mapper.xml
  • internal/db/sessions.go
  • internal/db/sessions_helpers.go
  • internal/db/sessions_mapper_test.go
  • internal/sandboxmount/path.go
  • internal/sessioneventfiles/references.go
  • internal/sessioneventfiles/references_test.go
  • internal/sessions/event_files.go
  • internal/sessions/file_resources.go
  • internal/sessions/service.go
  • internal/sessions/service_helpers.go
  • scripts/test-session-file-events-local.sh
  • tests/sessions_api_test.go

Comment thread docs/design/be/session-startup-message-delivery.md
Comment thread internal/codesessions/service.go Outdated
Comment thread internal/db/session_resource_mapper.xml
Comment thread tests/sessions_api_test.go Outdated

@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: 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 `@internal/deployments/handler.go`:
- Around line 765-777: Replace the map-based decoding in the deployment
resource-loading flow with a typed resource-reference DTO. Validate the DTO’s
type, file_id, and memory_store_id fields during unmarshalling or immediately
afterward, rejecting malformed or unsupported references instead of silently
converting failed assertions to empty strings. Update the resource loop and its
file/memory-store handling to consume the validated DTO fields.
🪄 Autofix

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 Plus

Run ID: 68188f75-ddce-4921-bb17-5d4c7b299cc1

📥 Commits

Reviewing files that changed from the base of the PR and between c25d24f and aaa5fe2.

📒 Files selected for processing (18)
  • docs/design/be/session-event-file-references.md
  • docs/design/be/session-startup-message-delivery.md
  • internal/codesessions/service.go
  • internal/db/session_event_mapper.go
  • internal/db/session_event_mapper.xml
  • internal/db/session_event_writes.go
  • internal/db/session_resource_mapper.xml
  • internal/db/sessions.go
  • internal/db/sessions_mapper_test.go
  • internal/deployments/handler.go
  • internal/deployments/resources.go
  • internal/sandboxmount/path.go
  • internal/sessioneventfiles/references.go
  • internal/sessioneventfiles/references_test.go
  • internal/sessions/event_files.go
  • internal/sessions/service.go
  • tests/deployments_api_test.go
  • tests/sessions_api_test.go
💤 Files with no reviewable changes (1)
  • internal/db/session_event_writes.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • internal/sandboxmount/path.go
  • internal/sessions/event_files.go
  • internal/db/session_resource_mapper.xml
  • internal/sessions/service.go
  • internal/sessioneventfiles/references_test.go
  • internal/sessioneventfiles/references.go
  • docs/design/be/session-event-file-references.md

Comment thread internal/deployments/handler.go Outdated

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

ℹ️ 本轮增量修复扎实,上一轮 review 的核心风险已被完整覆盖。仅发现一处与新增资源 pin 不变量相关的小边界不一致,见行内注释。

Reviewed changes
本轮对上一轮 pullfrog review(9e291f7)的反馈做了系统性修复,并补齐了对应测试:

  • Deployment run 路径在创建 Session 前,用本次运行物化出的 File 绑定校验 initial_events,未挂载/未出现在 deployment.resources 的引用以 session_resource_not_found_error 失败且不创建 Session(含 image MIME 校验)。
  • Realtime QueuePublicSessionEvents 改为使用 Send 事务内已校验的绑定快照,转换失败由静默 continue 改为返回错误并记录结构化日志。
  • 新增 HasFileReferenceForResource pin:被公开事件引用的 File Resource 删除返回 409(ErrFileInUse),与 Send 写入共用同一 Session 行锁,消除“已接受事件随后资源被删除”的竞态。
  • ListEventFileBindings 增加 expires_at 活动性过滤;WorkerPayload 校验失败返回 nil payload。
  • 测试修复:TestReferenceValidationFailures 增加 publicError 期望并补充 system.message 文件块用例(原先对两个挂载依赖 case 是恒真断言),失败场景测试前移;新增整批回滚、session_thread_id 拒绝、资源删除 409、Deployment run 成功/失败路径等测试。

设计文档同步更新了 realtime 绑定快照、资源 pin 与 Deployment run 校验的语义,且与实现一致。

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash𝕏

Comment thread internal/db/session_event_mapper.xml

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

ℹ️ d637632 改动干净,未引入新问题:把 Deployment run 校验里的松散 map[string]any 断言替换为带类型校验的解析器,并补齐了契约测试。

Reviewed changes
本轮增量(d637632 fix(deployments): type stored resource references)与上一轮审查内容独立,只覆盖 deployment 存储资源引用的解析健壮性:

  • 新增 parseDeploymentRunResourceReferences:用类型化 deploymentRunResourceReference 结构解析 deployment.Resources,按 file / memory_store / github_repository 分别校验对应引用字段,拒绝未知类型与跨类型字段污染。
  • validateRunReferences 改用该解析器,替代原先 resource["file_id"].(string) 这类会静默吞掉错误字段类型的断言。
  • TestParseDeploymentRunResourceReferences 覆盖 9 个拒绝用例与 1 个正常用例,失败场景先行,测试可证伪。

解析器与 sessionResourcesFromDeployment(materialize 路径)在同一份存储 JSON 上保持一致,github_repositoryvalidateRunReferences 中仍为无操作项,行为与旧实现对齐。未发现新问题。

上一轮针对 HasFileReferenceForResource 的 file_id 空白填充不对称发现仍然存在,且与本提交无关,保持开放。

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Flash𝕏

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

✅ 本轮增量(ebbd5bd merge + ff2a5ec)改动干净,未引入新问题。上一轮针对资源 pin 的空白不对称发现已在 ff2a5ec 从两侧闭合,新增测试可证伪。

Reviewed changes
本轮相对上一轮 pullfrog review(d637632)的增量是 ff2a5ec 及一次 main 合并:

  • 修复资源 pin 的空白不对称:referencesFromEvent 现在拒绝带首尾空白的非规范 file_idinternal/sessioneventfiles/references.go:150),HasFileReferenceForResource 的 pin 查询改用 BTRIM(...) 做防御性匹配(internal/db/session_event_mapper.xml:152),并同步更新了 Mapper SQL 断言与设计文档校验清单。
  • 补齐可证伪测试:TestReferenceValidationFailures 新增 "file ID has surrounding whitespace" 用例(publicError: true),失败路径先于成功用例。
  • 适配 main 的严格 DTO 解码(#229 风格):sessionMutationRequest 增加 InitialEvents 字段,createbody.InitialEvents 取代 fields["initial_events"]resourceFromRequest 直接读取 body.FileID / body.URL,与主分支 DecodeObjectBodyAs[T] 接线一致。
  • 本地验证:go build ./...go vet、受影响 package(sessioneventfiles/sandboxmount/deployments/sessions/codesessions/db)单元测试全部通过。

上一轮唯一的行内发现(pin SQL 空白不对称)已由本提交修复,相关 thread 已被作者标记解决;本 repo 的 Pullfrog 线程已全部关闭。

Pullfrog  | View workflow run | Using DeepSeek Flash𝕏

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

Caution

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

⚠️ Outside diff range comments (1)
internal/deployments/resources.go (1)

463-484: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reject duplicate file mounts with the same FileExternalID.

resolveReferences keeps the first binding for each FileID, so duplicate mounts resolve to the first path without an error.

🤖 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 `@internal/deployments/resources.go` around lines 463 - 484, Update
deploymentEventFileBindings to track each resource.FileMount.FileExternalID as
it is processed and return an error when the same ID appears more than once.
Perform this duplicate check before appending the binding, while preserving the
existing missing-file validation and binding behavior for unique mounts.
🤖 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 `@internal/deployments/resources.go`:
- Around line 361-378: Update the stored resource validation loop to reject
surrounding whitespace in both FileID and MemoryStoreID, matching the
canonical-ID checks used by the event-reference validator; require each present
ID to equal its trimmed value while preserving the existing type and
mutual-exclusion rules, and add table cases covering whitespace-padded IDs.

---

Outside diff comments:
In `@internal/deployments/resources.go`:
- Around line 463-484: Update deploymentEventFileBindings to track each
resource.FileMount.FileExternalID as it is processed and return an error when
the same ID appears more than once. Perform this duplicate check before
appending the binding, while preserving the existing missing-file validation and
binding behavior for unique mounts.
🪄 Autofix

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 Plus

Run ID: e9fa42d6-dc81-4114-bf90-24bc54b3df84

📥 Commits

Reviewing files that changed from the base of the PR and between aaa5fe2 and 6a7cd12.

📒 Files selected for processing (11)
  • docs/design/be/session-event-file-references.md
  • internal/db/session_event_mapper.xml
  • internal/db/sessions_mapper_test.go
  • internal/deployments/handler.go
  • internal/deployments/handler_contract_test.go
  • internal/deployments/resources.go
  • internal/sessioneventfiles/references.go
  • internal/sessioneventfiles/references_test.go
  • internal/sessions/handler.go
  • internal/sessions/service.go
  • internal/sessions/service_helpers.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • internal/sessioneventfiles/references_test.go
  • internal/db/sessions_mapper_test.go
  • internal/sessions/service_helpers.go
  • internal/db/session_event_mapper.xml
  • internal/sessioneventfiles/references.go
  • docs/design/be/session-event-file-references.md
  • internal/deployments/handler.go
  • internal/sessions/service.go

Comment thread internal/deployments/resources.go Outdated

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

ℹ️ 本轮增量删除本地真实服务 E2E 脚本 scripts/test-session-file-events-local.sh(459 行),未引入新逻辑,也未提供替代脚本;但设计文档「验收」一节与 PR 描述仍引用该脚本,存在失效引用,见行内注释。

Reviewed changes

  • 删除本地真实服务 E2E 脚本 scripts/test-session-file-events-local.sh,无替代实现;tests/e2e/ 下现存的 Files E2E(Go/Python/TS)均不覆盖“会话文件事件引用”链路。
  • 设计文档 session-event-file-references.md 的「验收」一节仍指导运行该脚本,未同步更新。

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash𝕏

Comment thread docs/design/be/session-event-file-references.md Outdated

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

ℹ️ 本轮增量把上一轮遗留的失效引用清掉了文档一半,改动干净。PR 描述中"增加真实本地服务 E2E 脚本"一句仍未更正,相关 thread 保持部分开放。

Reviewed changes

本轮增量(1056946)只改 docs/design/be/session-event-file-references.md

  • 删除「验收」一节中指导运行已删除的 scripts/test-session-file-events-local.sh 的整段说明(14 行),文档与仓库现状一致;全仓库 grep 已无该脚本引用。
  • 上一轮 thread(session-event-file-references.md:153)的文档一半已闭合,但 PR 描述「变更摘要」仍声称"增加真实本地服务 E2E 脚本"、「测试」节仍称"真实本地 E2E 通过",与删除后的仓库状态不符——这一半由 open thread 继续跟踪,待作者决定恢复脚本或更正描述。

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Flash𝕏

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

✅ No new issues found. 本轮增量把存储资源引用校验与事件引用校验的空白规范对齐,改动干净,测试可证伪。

Reviewed changes
本轮增量(b7f64f6)相对上一轮 pullfrog review(1056946)只改了 deployment 存储资源引用解析的空白校验:

  • parseDeploymentRunResourceReferences 拒绝带首尾空白的 IDfile_idmemory_store_id 现在要求 TrimSpace(v) == v,与 referencesFromEventff2a5ec)的 canonical-ID 校验对齐,消除"存储引用接受空白填充但事件引用校验拒绝"的不一致。
  • 新增两个可证伪测试用例"file ID has surrounding whitespace""memory store ID has surrounding whitespace" 加入拒绝列表,若解析器接受空白填充 ID 则测试会失败。

所有先前 Pullfrog 线程均已在 GitHub 上 [RESOLVED]。

Pullfrog  | View workflow run | Using openai-compatible/glm-5.2𝕏

…ile-references

# Conflicts:
#	internal/deployments/handler.go
#	internal/sessions/service.go

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

✅ No new issues found. 本轮增量只是把最新 main#232 错误适配器标准化)合入分支,冲突解决干净,PR 原有文件引用不变量全部保留。

Reviewed changes
本轮相对上一轮 pullfrog review(b7f64f6)的增量是 cfee78c merge,引入 main 的 #232 Standardize resource error adapters,并手写解决了 internal/deployments/handler.gointernal/sessions/service.go 的冲突:

  • Deployment runRoute 适配 error-returning 模式validateRunReferences 仍返回 (filesByID, referenceError)deploymentEventFileBindings 与逐条 ValidateMountedReferences 校验保留;writeRunReferenceFailure 改为返回 error(内部写 200 + failed run JSON 后返回 nil,不会与 ErrorAdapter 双写)。
  • Session sendEventsRoute / create / deleteResourceRoute 适配:Send 事务内的绑定快照 + QueuePublicSessionEvents(..., eventBindings) 接线不变;原先 deleteResourceRoute 内联的 ErrFileInUse→409 逻辑移入 mapResourceLoadErrorinternal/sessions/errors.go:125-127),线缆合同一致(409 conflict_error)。
  • internal/sessions/errors.go 新增 ErrFileInUse 映射,与 HasFileReferenceForResource pin 的既有测试契约吻合。

本地验证:go build ./... 与受影响 package(sessions/deployments/sessioneventfiles/sandboxmount/codesessions/httpapi)单元测试全部通过。设计文档无需更新(数据边界、pin 与校验语义均未变化)。

Pullfrog  | View workflow run | Using DeepSeek Flash𝕏

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

✅ No new issues found.

Reviewed changes
本轮相对上一轮 pullfrog review(cfee78c,4904493219)的增量是 ed16231 fix(sessions): align resource ID matching

  • 把资源 ID 匹配契约从"在校验边界拒绝带空白填充的 file_id"(ff2a5ec/b7f64f6 的 canonical 形式)统一为"原文保留 + 精确匹配":referencesFromEvent 只拒绝缺失或 trim 后为空的 file_id,带空白填充的值被保留,随后在挂载绑定精确匹配处落空并返回 400 "is not mounted"(internal/sessioneventfiles/references.go:146-150)。
  • HasFileReferenceForResource pin 查询去掉防御性 BTRIM,改为 content_block->'source'->>'file_id' = file.external_id 精确比对(internal/db/session_event_mapper.xml:152)。由于所有落库路径(send、创建 initial_events、deployment run)都先经过精确匹配的挂载校验,session_events.payload 中不可能存在带填充的 file_id,精确比对与存储数据一致,BTRIM 不再必要。
  • parseDeploymentRunResourceReferences 保留带空白填充的存储引用(仅拒绝 trim 后为空),填充值随后在 GetFileexternal_id 精确查询处使 run 失败,与事件侧行为一致(已核实 GetFile SQL 为精确匹配)。
  • 设计文档校验清单第 4 条与 Mapper SQL 断言同步更新为精确匹配契约;测试全部可证伪:all-whitespace 拒绝、padded file_id 精确匹配落空、"preserves resource IDs with surrounding whitespace" 契约用例、padded file_id 400 API 集成测试。

本地验证:go build ./... 通过;sessioneventfilesdeploymentsdbsessionscodesessionssandboxmount 单元测试全部通过。所有先前 Pullfrog 线程均已 [RESOLVED],PR 描述也已更正(不再声称存在本地 E2E 脚本)。

接受集合与上一版本完全相同(带空白填充的 ID 在两个版本下都被拒绝,仅错误文案从 "must not contain surrounding whitespace" 变为 "is not mounted");这是设计文档明确记载的有意取舍,三个校验面(事件引用、资源 pin、Deployment 存储引用)现在共用同一条精确匹配规则。

Pullfrog  | View workflow run | Using DeepSeek Flash𝕏

@sonarqubecloud

Copy link
Copy Markdown

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

ℹ️ 本轮增量(44d6763)把 worker 内容准备移入 Session 写事务并细化输入/内部错误分类,改动扎实、测试可证伪。仅发现一处与重构相关的失效文档注释,见文末 Nitpicks。

Reviewed changes
本轮相对上一轮 pullfrog review(b514451)的增量是 44d6763 fix(sessions): tighten event file processing

  • Worker 内容准备移入写事务events.sendWithSessionEventWriteTx 内用事务绑定快照为每条事件调用 prepareEventWorkerContent,全部准备成功才插入公开 payload;QueuePublicSessionEvents 不再重新解析 file_id/挂载路径,只按 event.ExternalID 取出已准备内容并补充 Code Session envelope(workerPayloadFromPublicEvent),消除了"公开事件已提交但 worker 内容未准备好"的中间状态——任一输入或转换失败整批回滚。
  • 输入/内部错误分类sessioneventfiles 新增 validationError/IsValidationErrorsessions 新增 eventInputError/eventProcessingErrormapEventProcessingError 把客户端输入错误映射为 400,把存量状态解析、挂载路径、序列化等内部错误映射为 500,同时应用于 events.send 与创建 initial_events
  • Deployment run 资源规划重构validateRunReferences 返回解析后的 []deploymentRunResource(保留 raw JSON),sessionResourcesFromDeploymentdeploymentEventFileBindings 合并为 planDeploymentSessionResources(一次解析,返回 resources + eventBindings 计划),行为与旧实现等价(secret 下标、filesByID 键、类型处理均保持一致)。
  • 测试:新增 TestMapEventProcessingErrorDistinguishesInputAndInternalFailuresTestNormalizeInputEventClassifiesInputAndInternalErrors、API 级 TestSessionEventInternalNormalizationFailureReturnsServerError(500 + 整批不落库)、TestPlanDeploymentSessionResourcesSharesFileBindingTestPlanSessionResourceWritesSharesFileBindingTestReferenceValidationFailures 补充断言 IsValidationError

本地验证:go build ./...go vet、受影响 package(codesessions/sessions/deployments/sessioneventfiles/sandboxmount/sessioncontract)单元测试全部通过。realtime 与 activation 的转换结果保持一致(prepared payload 与重新转换输出同一 envelope),未发现行为回归。

ℹ️ Nitpicks

  • internal/deployments/resources.go:85:160 两处文档注释仍引用已删除的 sessionResourcesFromDeployment,本提交已将其重命名为 planDeploymentSessionResources,建议同步更新注释,避免误导后续维护者。

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Flash𝕏

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