Add ability to attach extra files to feedback (#12370)

Allow clients to provide extra files.
This commit is contained in:
pakrym-oai 2026-02-20 14:26:14 -08:00 committed by GitHub
parent 9176f09cb8
commit 1bb7989b20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 58 additions and 13 deletions

View file

@ -507,6 +507,15 @@
"classification": {
"type": "string"
},
"extraLogFiles": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"includeLogs": {
"type": "boolean"
},

View file

@ -12549,6 +12549,15 @@
"classification": {
"type": "string"
},
"extraLogFiles": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"includeLogs": {
"type": "boolean"
},

View file

@ -4,6 +4,15 @@
"classification": {
"type": "string"
},
"extraLogFiles": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"includeLogs": {
"type": "boolean"
},

View file

@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type FeedbackUploadParams = { classification: string, reason?: string | null, threadId?: string | null, includeLogs: boolean, };
export type FeedbackUploadParams = { classification: string, reason?: string | null, threadId?: string | null, includeLogs: boolean, extraLogFiles?: Array<string> | null, };

View file

@ -1506,6 +1506,8 @@ pub struct FeedbackUploadParams {
#[ts(optional = nullable)]
pub thread_id: Option<String>,
pub include_logs: bool,
#[ts(optional = nullable)]
pub extra_log_files: Option<Vec<PathBuf>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]

View file

@ -150,7 +150,7 @@ Example with notification opt-out:
- `config/mcpServer/reload` — reload MCP server config from disk and queue a refresh for loaded threads (applied on each thread's next active turn); returns `{}`. Use this after editing `config.toml` without restarting the server.
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools, resources, resource templates, and auth status; supports cursor+limit pagination.
- `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); returns `{ started: true }` immediately and later emits `windowsSandbox/setupCompleted`.
- `feedback/upload` — submit a feedback report (classification + optional reason/logs and conversation_id); returns the tracking thread id.
- `feedback/upload` — submit a feedback report (classification + optional reason/logs, conversation_id, and optional `extraLogFiles` attachments array); returns the tracking thread id.
- `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
- `config/read` — fetch the effective config on disk after resolving config layering.
- `config/value/write` — write a single config key/value to the user's config.toml on disk.

View file

@ -6098,6 +6098,7 @@ impl CodexMessageProcessor {
reason,
thread_id,
include_logs,
extra_log_files,
} = params;
let conversation_id = match thread_id.as_deref() {
@ -6127,15 +6128,19 @@ impl CodexMessageProcessor {
} else {
None
};
let mut attachment_paths = validated_rollout_path.into_iter().collect::<Vec<_>>();
if let Some(extra_log_files) = extra_log_files {
attachment_paths.extend(extra_log_files);
}
let session_source = self.thread_manager.session_source();
let upload_result = tokio::task::spawn_blocking(move || {
let rollout_path_ref = validated_rollout_path.as_deref();
snapshot.upload_feedback(
&classification,
reason.as_deref(),
include_logs,
rollout_path_ref,
&attachment_paths,
Some(session_source),
)
})

View file

@ -224,7 +224,7 @@ impl CodexLogSnapshot {
classification: &str,
reason: Option<&str>,
include_logs: bool,
rollout_path: Option<&std::path::Path>,
extra_log_files: &[PathBuf],
session_source: Option<SessionSource>,
) -> Result<()> {
use std::collections::BTreeMap;
@ -317,11 +317,22 @@ impl CodexLogSnapshot {
}));
}
if let Some((path, data)) = rollout_path.and_then(|p| fs::read(p).ok().map(|d| (p, d))) {
for path in extra_log_files {
let data = match fs::read(path) {
Ok(data) => data,
Err(err) => {
tracing::warn!(
path = %path.display(),
error = %err,
"failed to read log attachment; skipping"
);
continue;
}
};
let fname = path
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_else(|| "rollout.jsonl".to_string());
.unwrap_or_else(|| "extra-log.log".to_string());
let content_type = "text/plain".to_string();
envelope.add_item(EnvelopeItem::Attachment(Attachment {
buffer: data,

View file

@ -87,7 +87,11 @@ impl FeedbackNoteView {
} else {
Some(note.as_str())
};
let rollout_path_ref = self.rollout_path.as_deref();
let log_file_paths = if self.include_logs {
self.rollout_path.iter().cloned().collect::<Vec<_>>()
} else {
Vec::new()
};
let classification = feedback_classification(self.category);
let mut thread_id = self.snapshot.thread_id.clone();
@ -96,11 +100,7 @@ impl FeedbackNoteView {
classification,
reason_opt,
self.include_logs,
if self.include_logs {
rollout_path_ref
} else {
None
},
&log_file_paths,
Some(SessionSource::Cli),
);