mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
Merge a73d681558 into sapling-pr-archive-bolinfest
This commit is contained in:
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+2
-2
@@ -8368,9 +8368,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.9"
|
||||
version = "0.103.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
|
||||
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
|
||||
@@ -3068,6 +3068,26 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadRealtimeTranscriptUpdatedNotification": {
|
||||
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"role",
|
||||
"text",
|
||||
"threadId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadStartedNotification": {
|
||||
"properties": {
|
||||
"thread": {
|
||||
@@ -4565,6 +4585,26 @@
|
||||
"title": "Thread/realtime/itemAddedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"thread/realtime/transcriptUpdated"
|
||||
],
|
||||
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/ThreadRealtimeTranscriptUpdatedNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Thread/realtime/transcriptUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
|
||||
@@ -4274,6 +4274,26 @@
|
||||
"title": "Thread/realtime/itemAddedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"thread/realtime/transcriptUpdated"
|
||||
],
|
||||
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/v2/ThreadRealtimeTranscriptUpdatedNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Thread/realtime/transcriptUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
@@ -13042,6 +13062,28 @@
|
||||
"title": "ThreadRealtimeStartedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadRealtimeTranscriptUpdatedNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"role",
|
||||
"text",
|
||||
"threadId"
|
||||
],
|
||||
"title": "ThreadRealtimeTranscriptUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadResumeParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
|
||||
|
||||
@@ -8709,6 +8709,26 @@
|
||||
"title": "Thread/realtime/itemAddedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"thread/realtime/transcriptUpdated"
|
||||
],
|
||||
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/ThreadRealtimeTranscriptUpdatedNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Thread/realtime/transcriptUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
@@ -10802,6 +10822,28 @@
|
||||
"title": "ThreadRealtimeStartedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadRealtimeTranscriptUpdatedNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"role",
|
||||
"text",
|
||||
"threadId"
|
||||
],
|
||||
"title": "ThreadRealtimeTranscriptUpdatedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadResumeParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"threadId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"role",
|
||||
"text",
|
||||
"threadId"
|
||||
],
|
||||
"title": "ThreadRealtimeTranscriptUpdatedNotification",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import type { ThreadRealtimeErrorNotification } from "./v2/ThreadRealtimeErrorNo
|
||||
import type { ThreadRealtimeItemAddedNotification } from "./v2/ThreadRealtimeItemAddedNotification";
|
||||
import type { ThreadRealtimeOutputAudioDeltaNotification } from "./v2/ThreadRealtimeOutputAudioDeltaNotification";
|
||||
import type { ThreadRealtimeStartedNotification } from "./v2/ThreadRealtimeStartedNotification";
|
||||
import type { ThreadRealtimeTranscriptUpdatedNotification } from "./v2/ThreadRealtimeTranscriptUpdatedNotification";
|
||||
import type { ThreadStartedNotification } from "./v2/ThreadStartedNotification";
|
||||
import type { ThreadStatusChangedNotification } from "./v2/ThreadStatusChangedNotification";
|
||||
import type { ThreadTokenUsageUpdatedNotification } from "./v2/ThreadTokenUsageUpdatedNotification";
|
||||
@@ -55,4 +56,4 @@ import type { WindowsWorldWritableWarningNotification } from "./v2/WindowsWorldW
|
||||
/**
|
||||
* Notification sent from the server to the client.
|
||||
*/
|
||||
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };
|
||||
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcriptUpdated", "params": ThreadRealtimeTranscriptUpdatedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL - flat transcript delta emitted whenever realtime
|
||||
* transcript text changes.
|
||||
*/
|
||||
export type ThreadRealtimeTranscriptUpdatedNotification = { threadId: string, role: string, text: string, };
|
||||
@@ -281,6 +281,7 @@ export type { ThreadRealtimeErrorNotification } from "./ThreadRealtimeErrorNotif
|
||||
export type { ThreadRealtimeItemAddedNotification } from "./ThreadRealtimeItemAddedNotification";
|
||||
export type { ThreadRealtimeOutputAudioDeltaNotification } from "./ThreadRealtimeOutputAudioDeltaNotification";
|
||||
export type { ThreadRealtimeStartedNotification } from "./ThreadRealtimeStartedNotification";
|
||||
export type { ThreadRealtimeTranscriptUpdatedNotification } from "./ThreadRealtimeTranscriptUpdatedNotification";
|
||||
export type { ThreadResumeParams } from "./ThreadResumeParams";
|
||||
export type { ThreadResumeResponse } from "./ThreadResumeResponse";
|
||||
export type { ThreadRollbackParams } from "./ThreadRollbackParams";
|
||||
|
||||
@@ -923,6 +923,8 @@ server_notification_definitions! {
|
||||
ThreadRealtimeStarted => "thread/realtime/started" (v2::ThreadRealtimeStartedNotification),
|
||||
#[experimental("thread/realtime/itemAdded")]
|
||||
ThreadRealtimeItemAdded => "thread/realtime/itemAdded" (v2::ThreadRealtimeItemAddedNotification),
|
||||
#[experimental("thread/realtime/transcriptUpdated")]
|
||||
ThreadRealtimeTranscriptUpdated => "thread/realtime/transcriptUpdated" (v2::ThreadRealtimeTranscriptUpdatedNotification),
|
||||
#[experimental("thread/realtime/outputAudio/delta")]
|
||||
ThreadRealtimeOutputAudioDelta => "thread/realtime/outputAudio/delta" (v2::ThreadRealtimeOutputAudioDeltaNotification),
|
||||
#[experimental("thread/realtime/error")]
|
||||
|
||||
@@ -3787,6 +3787,17 @@ pub struct ThreadRealtimeItemAddedNotification {
|
||||
pub item: JsonValue,
|
||||
}
|
||||
|
||||
/// EXPERIMENTAL - flat transcript delta emitted whenever realtime
|
||||
/// transcript text changes.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadRealtimeTranscriptUpdatedNotification {
|
||||
pub thread_id: String,
|
||||
pub role: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// EXPERIMENTAL - streamed output audio emitted by thread realtime.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -825,7 +825,8 @@ The fuzzy file search session API emits per-query notifications:
|
||||
The thread realtime API emits thread-scoped notifications for session lifecycle and streaming media:
|
||||
|
||||
- `thread/realtime/started` — `{ threadId, sessionId }` once realtime starts for the thread (experimental).
|
||||
- `thread/realtime/itemAdded` — `{ threadId, item }` for non-audio realtime items (experimental). `item` is forwarded as raw JSON while the upstream websocket item schema remains unstable.
|
||||
- `thread/realtime/itemAdded` — `{ threadId, item }` for raw non-audio realtime items that do not have a dedicated typed app-server notification, including `handoff_request` (experimental). `item` is forwarded as raw JSON while the upstream websocket item schema remains unstable.
|
||||
- `thread/realtime/transcriptUpdated` — `{ threadId, role, text }` whenever realtime transcript text changes (experimental). This forwards the live transcript delta from that realtime event, not the full accumulated transcript.
|
||||
- `thread/realtime/outputAudio/delta` — `{ threadId, audio }` for streamed output audio chunks (experimental). `audio` uses camelCase fields (`data`, `sampleRate`, `numChannels`, `samplesPerChannel`).
|
||||
- `thread/realtime/error` — `{ threadId, message }` when realtime encounters a transport or backend error (experimental).
|
||||
- `thread/realtime/closed` — `{ threadId, reason }` when the realtime transport closes (experimental).
|
||||
|
||||
@@ -86,6 +86,7 @@ use codex_app_server_protocol::ThreadRealtimeErrorNotification;
|
||||
use codex_app_server_protocol::ThreadRealtimeItemAddedNotification;
|
||||
use codex_app_server_protocol::ThreadRealtimeOutputAudioDeltaNotification;
|
||||
use codex_app_server_protocol::ThreadRealtimeStartedNotification;
|
||||
use codex_app_server_protocol::ThreadRealtimeTranscriptUpdatedNotification;
|
||||
use codex_app_server_protocol::ThreadRollbackResponse;
|
||||
use codex_app_server_protocol::ThreadTokenUsage;
|
||||
use codex_app_server_protocol::ThreadTokenUsageUpdatedNotification;
|
||||
@@ -397,8 +398,30 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
))
|
||||
.await;
|
||||
}
|
||||
RealtimeEvent::InputTranscriptDelta(_) => {}
|
||||
RealtimeEvent::OutputTranscriptDelta(_) => {}
|
||||
RealtimeEvent::InputTranscriptDelta(event) => {
|
||||
let notification = ThreadRealtimeTranscriptUpdatedNotification {
|
||||
thread_id: conversation_id.to_string(),
|
||||
role: "user".to_string(),
|
||||
text: event.delta,
|
||||
};
|
||||
outgoing
|
||||
.send_server_notification(
|
||||
ServerNotification::ThreadRealtimeTranscriptUpdated(notification),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
RealtimeEvent::OutputTranscriptDelta(event) => {
|
||||
let notification = ThreadRealtimeTranscriptUpdatedNotification {
|
||||
thread_id: conversation_id.to_string(),
|
||||
role: "assistant".to_string(),
|
||||
text: event.delta,
|
||||
};
|
||||
outgoing
|
||||
.send_server_notification(
|
||||
ServerNotification::ThreadRealtimeTranscriptUpdated(notification),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
RealtimeEvent::AudioOut(audio) => {
|
||||
let notification = ThreadRealtimeOutputAudioDeltaNotification {
|
||||
thread_id: conversation_id.to_string(),
|
||||
|
||||
@@ -21,6 +21,7 @@ use codex_app_server_protocol::ThreadRealtimeStartResponse;
|
||||
use codex_app_server_protocol::ThreadRealtimeStartedNotification;
|
||||
use codex_app_server_protocol::ThreadRealtimeStopParams;
|
||||
use codex_app_server_protocol::ThreadRealtimeStopResponse;
|
||||
use codex_app_server_protocol::ThreadRealtimeTranscriptUpdatedNotification;
|
||||
use codex_app_server_protocol::ThreadStartParams;
|
||||
use codex_app_server_protocol::ThreadStartResponse;
|
||||
use codex_features::FEATURES;
|
||||
@@ -66,6 +67,24 @@ async fn realtime_conversation_streams_v2_notifications() -> Result<()> {
|
||||
"content": [{ "type": "text", "text": "hi" }]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"type": "conversation.item.input_audio_transcription.delta",
|
||||
"delta": "delegate now"
|
||||
}),
|
||||
json!({
|
||||
"type": "response.output_text.delta",
|
||||
"delta": "working"
|
||||
}),
|
||||
json!({
|
||||
"type": "conversation.item.done",
|
||||
"item": {
|
||||
"id": "item_2",
|
||||
"type": "function_call",
|
||||
"name": "codex",
|
||||
"call_id": "handoff_1",
|
||||
"arguments": "{\"input_transcript\":\"delegate now\"}"
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"type": "error",
|
||||
"message": "upstream boom"
|
||||
@@ -180,6 +199,40 @@ async fn realtime_conversation_streams_v2_notifications() -> Result<()> {
|
||||
assert_eq!(item_added.thread_id, output_audio.thread_id);
|
||||
assert_eq!(item_added.item["type"], json!("message"));
|
||||
|
||||
let first_transcript_update = read_notification::<ThreadRealtimeTranscriptUpdatedNotification>(
|
||||
&mut mcp,
|
||||
"thread/realtime/transcriptUpdated",
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(first_transcript_update.thread_id, output_audio.thread_id);
|
||||
assert_eq!(first_transcript_update.role, "user");
|
||||
assert_eq!(first_transcript_update.text, "delegate now");
|
||||
|
||||
let second_transcript_update =
|
||||
read_notification::<ThreadRealtimeTranscriptUpdatedNotification>(
|
||||
&mut mcp,
|
||||
"thread/realtime/transcriptUpdated",
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(second_transcript_update.thread_id, output_audio.thread_id);
|
||||
assert_eq!(second_transcript_update.role, "assistant");
|
||||
assert_eq!(second_transcript_update.text, "working");
|
||||
|
||||
let handoff_item_added = read_notification::<ThreadRealtimeItemAddedNotification>(
|
||||
&mut mcp,
|
||||
"thread/realtime/itemAdded",
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(handoff_item_added.thread_id, output_audio.thread_id);
|
||||
assert_eq!(handoff_item_added.item["type"], json!("handoff_request"));
|
||||
assert_eq!(handoff_item_added.item["handoff_id"], json!("handoff_1"));
|
||||
assert_eq!(handoff_item_added.item["item_id"], json!("item_2"));
|
||||
assert_eq!(
|
||||
handoff_item_added.item["input_transcript"],
|
||||
json!("delegate now")
|
||||
);
|
||||
assert_eq!(handoff_item_added.item["active_transcript"], json!([]));
|
||||
|
||||
let realtime_error =
|
||||
read_notification::<ThreadRealtimeErrorNotification>(&mut mcp, "thread/realtime/error")
|
||||
.await?;
|
||||
|
||||
@@ -309,14 +309,16 @@ pub fn prepend_path_entry_for_codex_aliases() -> std::io::Result<Arg0PathEntryGu
|
||||
#[cfg(windows)]
|
||||
const PATH_SEPARATOR: &str = ";";
|
||||
|
||||
let path_element = path.display();
|
||||
let updated_path_env_var = match std::env::var("PATH") {
|
||||
Ok(existing_path) => {
|
||||
format!("{path_element}{PATH_SEPARATOR}{existing_path}")
|
||||
}
|
||||
Err(_) => {
|
||||
format!("{path_element}")
|
||||
let updated_path_env_var = match std::env::var_os("PATH") {
|
||||
Some(existing_path) => {
|
||||
let mut path_env_var =
|
||||
std::ffi::OsString::with_capacity(path.as_os_str().len() + 1 + existing_path.len());
|
||||
path_env_var.push(path);
|
||||
path_env_var.push(PATH_SEPARATOR);
|
||||
path_env_var.push(existing_path);
|
||||
path_env_var
|
||||
}
|
||||
None => path.as_os_str().to_owned(),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
|
||||
@@ -100,6 +100,7 @@ use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::config::permissions::compile_permission_profile;
|
||||
use crate::config::permissions::get_readable_roots_required_for_codex_runtime;
|
||||
use crate::config::permissions::network_proxy_config_from_profile_network;
|
||||
use crate::config::profile::ConfigProfile;
|
||||
use codex_network_proxy::NetworkProxyConfig;
|
||||
@@ -1945,29 +1946,6 @@ fn resolve_permission_config_syntax(
|
||||
})
|
||||
}
|
||||
|
||||
fn add_additional_file_system_writes(
|
||||
file_system_sandbox_policy: &mut FileSystemSandboxPolicy,
|
||||
additional_writable_roots: &[AbsolutePathBuf],
|
||||
) {
|
||||
for path in additional_writable_roots {
|
||||
let exists = file_system_sandbox_policy.entries.iter().any(|entry| {
|
||||
matches!(
|
||||
&entry.path,
|
||||
codex_protocol::permissions::FileSystemPath::Path { path: existing }
|
||||
if existing == path && entry.access == codex_protocol::permissions::FileSystemAccessMode::Write
|
||||
)
|
||||
});
|
||||
if !exists {
|
||||
file_system_sandbox_policy.entries.push(
|
||||
codex_protocol::permissions::FileSystemSandboxEntry {
|
||||
path: codex_protocol::permissions::FileSystemPath::Path { path: path.clone() },
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Write,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional overrides for user configuration (e.g., from CLI flags).
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct ConfigOverrides {
|
||||
@@ -2336,10 +2314,8 @@ impl Config {
|
||||
let mut sandbox_policy = file_system_sandbox_policy
|
||||
.to_legacy_sandbox_policy(network_sandbox_policy, &resolved_cwd)?;
|
||||
if matches!(sandbox_policy, SandboxPolicy::WorkspaceWrite { .. }) {
|
||||
add_additional_file_system_writes(
|
||||
&mut file_system_sandbox_policy,
|
||||
&additional_writable_roots,
|
||||
);
|
||||
file_system_sandbox_policy = file_system_sandbox_policy
|
||||
.with_additional_writable_roots(&resolved_cwd, &additional_writable_roots);
|
||||
sandbox_policy = file_system_sandbox_policy
|
||||
.to_legacy_sandbox_policy(network_sandbox_policy, &resolved_cwd)?;
|
||||
}
|
||||
@@ -2690,6 +2666,11 @@ impl Config {
|
||||
} else {
|
||||
network.enabled().then_some(network)
|
||||
};
|
||||
let helper_readable_roots = get_readable_roots_required_for_codex_runtime(
|
||||
&codex_home,
|
||||
zsh_path.as_ref(),
|
||||
main_execve_wrapper_exe.as_ref(),
|
||||
);
|
||||
let effective_sandbox_policy = constrained_sandbox_policy.value.get().clone();
|
||||
let effective_file_system_sandbox_policy =
|
||||
if effective_sandbox_policy == original_sandbox_policy {
|
||||
@@ -2700,6 +2681,8 @@ impl Config {
|
||||
&resolved_cwd,
|
||||
)
|
||||
};
|
||||
let effective_file_system_sandbox_policy = effective_file_system_sandbox_policy
|
||||
.with_additional_readable_roots(&resolved_cwd, &helper_readable_roots);
|
||||
let effective_network_sandbox_policy =
|
||||
if effective_sandbox_policy == original_sandbox_policy {
|
||||
network_sandbox_policy
|
||||
|
||||
@@ -190,6 +190,37 @@ pub(crate) fn compile_permission_profile(
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns a list of paths that must be readable by shell tools in order
|
||||
/// for Codex to function. These should always be added to the
|
||||
/// `FileSystemSandboxPolicy` for a thread.
|
||||
pub(crate) fn get_readable_roots_required_for_codex_runtime(
|
||||
codex_home: &Path,
|
||||
zsh_path: Option<&PathBuf>,
|
||||
main_execve_wrapper_exe: Option<&PathBuf>,
|
||||
) -> Vec<AbsolutePathBuf> {
|
||||
let arg0_root = AbsolutePathBuf::from_absolute_path(codex_home.join("tmp").join("arg0")).ok();
|
||||
let zsh_path = zsh_path.and_then(|path| AbsolutePathBuf::from_absolute_path(path).ok());
|
||||
let execve_wrapper_root = main_execve_wrapper_exe.and_then(|path| {
|
||||
let path = AbsolutePathBuf::from_absolute_path(path).ok()?;
|
||||
if let Some(arg0_root) = arg0_root.as_ref()
|
||||
&& path.as_path().starts_with(arg0_root.as_path())
|
||||
{
|
||||
path.parent()
|
||||
} else {
|
||||
Some(path)
|
||||
}
|
||||
});
|
||||
|
||||
let mut readable_roots = Vec::new();
|
||||
if let Some(zsh_path) = zsh_path {
|
||||
readable_roots.push(zsh_path);
|
||||
}
|
||||
if let Some(execve_wrapper_root) = execve_wrapper_root {
|
||||
readable_roots.push(execve_wrapper_root);
|
||||
}
|
||||
readable_roots
|
||||
}
|
||||
|
||||
fn compile_network_sandbox_policy(network: Option<&NetworkToml>) -> NetworkSandboxPolicy {
|
||||
let Some(network) = network else {
|
||||
return NetworkSandboxPolicy::Restricted;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use super::*;
|
||||
use crate::config::Config;
|
||||
use crate::config::ConfigOverrides;
|
||||
use crate::config::ConfigToml;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::BTreeMap;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn normalize_absolute_path_for_platform_simplifies_windows_verbatim_paths() {
|
||||
@@ -7,3 +13,66 @@ fn normalize_absolute_path_for_platform_simplifies_windows_verbatim_paths() {
|
||||
normalize_absolute_path_for_platform(r"\\?\D:\c\x\worktrees\2508\swift-base", true);
|
||||
assert_eq!(parsed, PathBuf::from(r"D:\c\x\worktrees\2508\swift-base"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restricted_read_implicitly_allows_helper_executables() -> std::io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let cwd = temp_dir.path().join("workspace");
|
||||
let codex_home = temp_dir.path().join(".codex");
|
||||
let zsh_path = temp_dir.path().join("runtime").join("zsh");
|
||||
let arg0_root = codex_home.join("tmp").join("arg0");
|
||||
let allowed_arg0_dir = arg0_root.join("codex-arg0-session");
|
||||
let sibling_arg0_dir = arg0_root.join("codex-arg0-other-session");
|
||||
let execve_wrapper = allowed_arg0_dir.join("codex-execve-wrapper");
|
||||
std::fs::create_dir_all(&cwd)?;
|
||||
std::fs::create_dir_all(zsh_path.parent().expect("zsh path should have parent"))?;
|
||||
std::fs::create_dir_all(&allowed_arg0_dir)?;
|
||||
std::fs::create_dir_all(&sibling_arg0_dir)?;
|
||||
std::fs::write(&zsh_path, "")?;
|
||||
std::fs::write(&execve_wrapper, "")?;
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml {
|
||||
default_permissions: Some("workspace".to_string()),
|
||||
permissions: Some(PermissionsToml {
|
||||
entries: BTreeMap::from([(
|
||||
"workspace".to_string(),
|
||||
PermissionProfileToml {
|
||||
filesystem: Some(FilesystemPermissionsToml {
|
||||
entries: BTreeMap::new(),
|
||||
}),
|
||||
network: None,
|
||||
},
|
||||
)]),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
ConfigOverrides {
|
||||
cwd: Some(cwd.clone()),
|
||||
zsh_path: Some(zsh_path.clone()),
|
||||
main_execve_wrapper_exe: Some(execve_wrapper),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home,
|
||||
)?;
|
||||
|
||||
let expected_zsh = AbsolutePathBuf::try_from(zsh_path)?;
|
||||
let expected_allowed_arg0_dir = AbsolutePathBuf::try_from(allowed_arg0_dir)?;
|
||||
let expected_sibling_arg0_dir = AbsolutePathBuf::try_from(sibling_arg0_dir)?;
|
||||
let policy = &config.permissions.file_system_sandbox_policy;
|
||||
|
||||
assert!(
|
||||
policy.can_read_path_with_cwd(expected_zsh.as_path(), &cwd),
|
||||
"expected zsh helper path to be readable, policy: {policy:?}"
|
||||
);
|
||||
assert!(
|
||||
policy.can_read_path_with_cwd(expected_allowed_arg0_dir.as_path(), &cwd),
|
||||
"expected active arg0 helper dir to be readable, policy: {policy:?}"
|
||||
);
|
||||
assert!(
|
||||
!policy.can_read_path_with_cwd(expected_sibling_arg0_dir.as_path(), &cwd),
|
||||
"expected sibling arg0 helper dir to remain unreadable, policy: {policy:?}"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -73,12 +73,6 @@ ignore = [
|
||||
{ id = "RUSTSEC-2024-0388", reason = "derivative is unmaintained; pulled in via starlark v0.13.0 used by execpolicy/cli/core; no fixed release yet" },
|
||||
{ id = "RUSTSEC-2025-0057", reason = "fxhash is unmaintained; pulled in via starlark_map/starlark v0.13.0 used by execpolicy/cli/core; no fixed release yet" },
|
||||
{ id = "RUSTSEC-2024-0436", reason = "paste is unmaintained; pulled in via ratatui/rmcp/starlark used by tui/execpolicy; no fixed release yet" },
|
||||
# TODO(fcoury): remove these exceptions when the aws-lc-sys upgrade path is Bazel-compatible in this workspace.
|
||||
{ id = "RUSTSEC-2026-0044", reason = "aws-lc-sys remains on 0.37.0 because upgrading currently breaks Bazel fetch/build for this workspace" },
|
||||
{ id = "RUSTSEC-2026-0045", reason = "aws-lc-sys remains on 0.37.0 because upgrading currently breaks Bazel fetch/build for this workspace" },
|
||||
{ id = "RUSTSEC-2026-0046", reason = "aws-lc-sys remains on 0.37.0 because upgrading currently breaks Bazel fetch/build for this workspace" },
|
||||
{ id = "RUSTSEC-2026-0047", reason = "aws-lc-sys remains on 0.37.0 because upgrading currently breaks Bazel fetch/build for this workspace" },
|
||||
{ id = "RUSTSEC-2026-0048", reason = "aws-lc-sys remains on 0.37.0 because upgrading currently breaks Bazel fetch/build for this workspace" },
|
||||
# TODO(fcoury): remove this exception when syntect drops yaml-rust and bincode, or updates to versions that have fixed the vulnerabilities.
|
||||
{ id = "RUSTSEC-2024-0320", reason = "yaml-rust is unmaintained; pulled in via syntect v5.3.0 used by codex-tui for syntax highlighting; no fixed release yet" },
|
||||
{ id = "RUSTSEC-2025-0141", reason = "bincode is unmaintained; pulled in via syntect v5.3.0 used by codex-tui for syntax highlighting; no fixed release yet" },
|
||||
|
||||
@@ -478,7 +478,7 @@ const APPROVAL_POLICY_UNLESS_TRUSTED: &str =
|
||||
const APPROVAL_POLICY_ON_FAILURE: &str =
|
||||
include_str!("prompts/permissions/approval_policy/on_failure.md");
|
||||
const APPROVAL_POLICY_ON_REQUEST_RULE: &str =
|
||||
include_str!("prompts/permissions/approval_policy/on_request_rule.md");
|
||||
include_str!("prompts/permissions/approval_policy/on_request.md");
|
||||
const APPROVAL_POLICY_ON_REQUEST_RULE_REQUEST_PERMISSION: &str =
|
||||
include_str!("prompts/permissions/approval_policy/on_request_rule_request_permission.md");
|
||||
|
||||
|
||||
@@ -347,6 +347,48 @@ impl FileSystemSandboxPolicy {
|
||||
self.resolve_access_with_cwd(path, cwd).can_write()
|
||||
}
|
||||
|
||||
pub fn with_additional_readable_roots(
|
||||
mut self,
|
||||
cwd: &Path,
|
||||
additional_readable_roots: &[AbsolutePathBuf],
|
||||
) -> Self {
|
||||
if self.has_full_disk_read_access() {
|
||||
return self;
|
||||
}
|
||||
|
||||
for path in additional_readable_roots {
|
||||
if self.can_read_path_with_cwd(path.as_path(), cwd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.entries.push(FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: path.clone() },
|
||||
access: FileSystemAccessMode::Read,
|
||||
});
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_additional_writable_roots(
|
||||
mut self,
|
||||
cwd: &Path,
|
||||
additional_writable_roots: &[AbsolutePathBuf],
|
||||
) -> Self {
|
||||
for path in additional_writable_roots {
|
||||
if self.can_write_path_with_cwd(path.as_path(), cwd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.entries.push(FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: path.clone() },
|
||||
access: FileSystemAccessMode::Write,
|
||||
});
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn needs_direct_runtime_enforcement(
|
||||
&self,
|
||||
network_policy: NetworkSandboxPolicy,
|
||||
@@ -1782,6 +1824,74 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_additional_readable_roots_skips_existing_effective_access() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let cwd_root = AbsolutePathBuf::from_absolute_path(cwd.path()).expect("absolute cwd");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
}]);
|
||||
|
||||
let actual = policy
|
||||
.clone()
|
||||
.with_additional_readable_roots(cwd.path(), std::slice::from_ref(&cwd_root));
|
||||
|
||||
assert_eq!(actual, policy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_additional_writable_roots_skips_existing_effective_access() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let cwd_root = AbsolutePathBuf::from_absolute_path(cwd.path()).expect("absolute cwd");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
|
||||
let actual = policy
|
||||
.clone()
|
||||
.with_additional_writable_roots(cwd.path(), std::slice::from_ref(&cwd_root));
|
||||
|
||||
assert_eq!(actual, policy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_additional_writable_roots_adds_new_root() {
|
||||
let temp_dir = TempDir::new().expect("tempdir");
|
||||
let cwd = temp_dir.path().join("workspace");
|
||||
let extra = AbsolutePathBuf::from_absolute_path(temp_dir.path().join("extra"))
|
||||
.expect("resolve extra root");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
|
||||
let actual = policy.with_additional_writable_roots(&cwd, std::slice::from_ref(&extra));
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: extra },
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_system_access_mode_orders_by_conflict_precedence() {
|
||||
assert!(FileSystemAccessMode::Write > FileSystemAccessMode::Read);
|
||||
|
||||
+3
-2
@@ -19,6 +19,8 @@ This is treated as two command segments:
|
||||
|
||||
["tee", "output.txt"]
|
||||
|
||||
Commands that use more advanced shell features like redirection (>, >>, <), substitutions ($(...), ...), environment variables (FOO=bar), or wildcard patterns (*, ?) will not be evaluated against rules, to limit the scope of what an approved rule allows.
|
||||
|
||||
## How to request escalation
|
||||
|
||||
IMPORTANT: To request approval to execute a command that will require escalated privileges:
|
||||
@@ -44,7 +46,7 @@ While commands are running inside the sandbox, here are some scenarios that will
|
||||
When choosing a `prefix_rule`, request one that will allow you to fulfill similar requests from the user in the future without re-requesting escalation. It should be categorical and reasonably scoped to similar capabilities. You should rarely pass the entire command into `prefix_rule`.
|
||||
|
||||
### Banned prefix_rules
|
||||
Avoid requesting overly broad prefixes that the user would be ill-advised to approve. For example, do not request ["python3"], ["python", "-"], or other similar prefixes.
|
||||
Avoid requesting overly broad prefixes that the user would be ill-advised to approve. For example, do not request ["python3"], ["python", "-"], or other similar prefixes that would allow arbitrary scripting.
|
||||
NEVER provide a prefix_rule argument for destructive commands like rm.
|
||||
NEVER provide a prefix_rule if your command uses a heredoc or herestring.
|
||||
|
||||
@@ -52,5 +54,4 @@ NEVER provide a prefix_rule if your command uses a heredoc or herestring.
|
||||
Good examples of prefixes:
|
||||
- ["npm", "run", "dev"]
|
||||
- ["gh", "pr", "check"]
|
||||
- ["pytest"]
|
||||
- ["cargo", "test"]
|
||||
@@ -403,6 +403,7 @@ pub(crate) struct ChatComposer {
|
||||
config: ChatComposerConfig,
|
||||
collaboration_mode_indicator: Option<CollaborationModeIndicator>,
|
||||
connectors_enabled: bool,
|
||||
plugins_command_enabled: bool,
|
||||
fast_command_enabled: bool,
|
||||
personality_command_enabled: bool,
|
||||
realtime_conversation_enabled: bool,
|
||||
@@ -441,6 +442,7 @@ impl ChatComposer {
|
||||
BuiltinCommandFlags {
|
||||
collaboration_modes_enabled: self.collaboration_modes_enabled,
|
||||
connectors_enabled: self.connectors_enabled,
|
||||
plugins_command_enabled: self.plugins_command_enabled,
|
||||
fast_command_enabled: self.fast_command_enabled,
|
||||
personality_command_enabled: self.personality_command_enabled,
|
||||
realtime_conversation_enabled: self.realtime_conversation_enabled,
|
||||
@@ -525,6 +527,7 @@ impl ChatComposer {
|
||||
config,
|
||||
collaboration_mode_indicator: None,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: false,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -559,6 +562,10 @@ impl ChatComposer {
|
||||
self.sync_popups();
|
||||
}
|
||||
|
||||
pub fn set_plugins_command_enabled(&mut self, enabled: bool) {
|
||||
self.plugins_command_enabled = enabled;
|
||||
}
|
||||
|
||||
/// Toggle composer-side image paste handling.
|
||||
///
|
||||
/// This only affects whether image-like paste content is converted into attachments; the
|
||||
@@ -3477,6 +3484,7 @@ impl ChatComposer {
|
||||
if is_editing_slash_command_name {
|
||||
let collaboration_modes_enabled = self.collaboration_modes_enabled;
|
||||
let connectors_enabled = self.connectors_enabled;
|
||||
let plugins_command_enabled = self.plugins_command_enabled;
|
||||
let fast_command_enabled = self.fast_command_enabled;
|
||||
let personality_command_enabled = self.personality_command_enabled;
|
||||
let realtime_conversation_enabled = self.realtime_conversation_enabled;
|
||||
@@ -3486,6 +3494,7 @@ impl ChatComposer {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled,
|
||||
connectors_enabled,
|
||||
plugins_command_enabled,
|
||||
fast_command_enabled,
|
||||
personality_command_enabled,
|
||||
realtime_conversation_enabled,
|
||||
|
||||
@@ -38,6 +38,7 @@ pub(crate) struct CommandPopup {
|
||||
pub(crate) struct CommandPopupFlags {
|
||||
pub(crate) collaboration_modes_enabled: bool,
|
||||
pub(crate) connectors_enabled: bool,
|
||||
pub(crate) plugins_command_enabled: bool,
|
||||
pub(crate) fast_command_enabled: bool,
|
||||
pub(crate) personality_command_enabled: bool,
|
||||
pub(crate) realtime_conversation_enabled: bool,
|
||||
@@ -50,6 +51,7 @@ impl From<CommandPopupFlags> for slash_commands::BuiltinCommandFlags {
|
||||
Self {
|
||||
collaboration_modes_enabled: value.collaboration_modes_enabled,
|
||||
connectors_enabled: value.connectors_enabled,
|
||||
plugins_command_enabled: value.plugins_command_enabled,
|
||||
fast_command_enabled: value.fast_command_enabled,
|
||||
personality_command_enabled: value.personality_command_enabled,
|
||||
realtime_conversation_enabled: value.realtime_conversation_enabled,
|
||||
@@ -509,6 +511,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -531,6 +534,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -553,6 +557,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: false,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -583,6 +588,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -605,6 +611,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: false,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: true,
|
||||
|
||||
@@ -266,6 +266,11 @@ impl BottomPane {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub fn set_plugins_command_enabled(&mut self, enabled: bool) {
|
||||
self.composer.set_plugins_command_enabled(enabled);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub fn take_mention_bindings(&mut self) -> Vec<MentionBinding> {
|
||||
self.composer.take_mention_bindings()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::slash_command::built_in_slash_commands;
|
||||
pub(crate) struct BuiltinCommandFlags {
|
||||
pub(crate) collaboration_modes_enabled: bool,
|
||||
pub(crate) connectors_enabled: bool,
|
||||
pub(crate) plugins_command_enabled: bool,
|
||||
pub(crate) fast_command_enabled: bool,
|
||||
pub(crate) personality_command_enabled: bool,
|
||||
pub(crate) realtime_conversation_enabled: bool,
|
||||
@@ -31,6 +32,7 @@ pub(crate) fn builtins_for_input(flags: BuiltinCommandFlags) -> Vec<(&'static st
|
||||
|| !matches!(*cmd, SlashCommand::Collab | SlashCommand::Plan)
|
||||
})
|
||||
.filter(|(_, cmd)| flags.connectors_enabled || *cmd != SlashCommand::Apps)
|
||||
.filter(|(_, cmd)| flags.plugins_command_enabled || *cmd != SlashCommand::Plugins)
|
||||
.filter(|(_, cmd)| flags.fast_command_enabled || *cmd != SlashCommand::Fast)
|
||||
.filter(|(_, cmd)| flags.personality_command_enabled || *cmd != SlashCommand::Personality)
|
||||
.filter(|(_, cmd)| flags.realtime_conversation_enabled || *cmd != SlashCommand::Realtime)
|
||||
@@ -63,6 +65,7 @@ mod tests {
|
||||
BuiltinCommandFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: true,
|
||||
plugins_command_enabled: true,
|
||||
fast_command_enabled: true,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: true,
|
||||
|
||||
@@ -1430,6 +1430,7 @@ impl ChatWidget {
|
||||
self.refresh_model_display();
|
||||
self.sync_fast_command_enabled();
|
||||
self.sync_personality_command_enabled();
|
||||
self.sync_plugins_command_enabled();
|
||||
self.refresh_plugin_mentions();
|
||||
let startup_tooltip_override = self.startup_tooltip_override.take();
|
||||
let show_fast_status = self.should_show_fast_status(&model_for_header, event.service_tier);
|
||||
@@ -3738,6 +3739,7 @@ impl ChatWidget {
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget.sync_plugins_command_enabled();
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_queued_message_edit_binding(widget.queued_message_edit_binding);
|
||||
@@ -3938,6 +3940,7 @@ impl ChatWidget {
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget.sync_plugins_command_enabled();
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_queued_message_edit_binding(widget.queued_message_edit_binding);
|
||||
@@ -4130,6 +4133,7 @@ impl ChatWidget {
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget.sync_plugins_command_enabled();
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_queued_message_edit_binding(widget.queued_message_edit_binding);
|
||||
@@ -7823,6 +7827,7 @@ impl ChatWidget {
|
||||
self.sync_personality_command_enabled();
|
||||
}
|
||||
if feature == Feature::Plugins {
|
||||
self.sync_plugins_command_enabled();
|
||||
self.refresh_plugin_mentions();
|
||||
}
|
||||
if feature == Feature::PreventIdleSleep {
|
||||
@@ -8025,6 +8030,11 @@ impl ChatWidget {
|
||||
.set_personality_command_enabled(self.config.features.enabled(Feature::Personality));
|
||||
}
|
||||
|
||||
fn sync_plugins_command_enabled(&mut self) {
|
||||
self.bottom_pane
|
||||
.set_plugins_command_enabled(self.config.features.enabled(Feature::Plugins));
|
||||
}
|
||||
|
||||
fn current_model_supports_personality(&self) -> bool {
|
||||
let model = self.current_model();
|
||||
self.models_manager
|
||||
|
||||
@@ -482,6 +482,9 @@ fn server_notification_thread_target(
|
||||
ServerNotification::ThreadRealtimeItemAdded(notification) => {
|
||||
Some(notification.thread_id.as_str())
|
||||
}
|
||||
ServerNotification::ThreadRealtimeTranscriptUpdated(notification) => {
|
||||
Some(notification.thread_id.as_str())
|
||||
}
|
||||
ServerNotification::ThreadRealtimeOutputAudioDelta(notification) => {
|
||||
Some(notification.thread_id.as_str())
|
||||
}
|
||||
|
||||
@@ -403,6 +403,7 @@ pub(crate) struct ChatComposer {
|
||||
config: ChatComposerConfig,
|
||||
collaboration_mode_indicator: Option<CollaborationModeIndicator>,
|
||||
connectors_enabled: bool,
|
||||
plugins_command_enabled: bool,
|
||||
fast_command_enabled: bool,
|
||||
personality_command_enabled: bool,
|
||||
realtime_conversation_enabled: bool,
|
||||
@@ -441,6 +442,7 @@ impl ChatComposer {
|
||||
BuiltinCommandFlags {
|
||||
collaboration_modes_enabled: self.collaboration_modes_enabled,
|
||||
connectors_enabled: self.connectors_enabled,
|
||||
plugins_command_enabled: self.plugins_command_enabled,
|
||||
fast_command_enabled: self.fast_command_enabled,
|
||||
personality_command_enabled: self.personality_command_enabled,
|
||||
realtime_conversation_enabled: self.realtime_conversation_enabled,
|
||||
@@ -525,6 +527,7 @@ impl ChatComposer {
|
||||
config,
|
||||
collaboration_mode_indicator: None,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: false,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -559,6 +562,10 @@ impl ChatComposer {
|
||||
self.sync_popups();
|
||||
}
|
||||
|
||||
pub fn set_plugins_command_enabled(&mut self, enabled: bool) {
|
||||
self.plugins_command_enabled = enabled;
|
||||
}
|
||||
|
||||
/// Toggle composer-side image paste handling.
|
||||
///
|
||||
/// This only affects whether image-like paste content is converted into attachments; the
|
||||
@@ -3491,6 +3498,7 @@ impl ChatComposer {
|
||||
if is_editing_slash_command_name {
|
||||
let collaboration_modes_enabled = self.collaboration_modes_enabled;
|
||||
let connectors_enabled = self.connectors_enabled;
|
||||
let plugins_command_enabled = self.plugins_command_enabled;
|
||||
let fast_command_enabled = self.fast_command_enabled;
|
||||
let personality_command_enabled = self.personality_command_enabled;
|
||||
let realtime_conversation_enabled = self.realtime_conversation_enabled;
|
||||
@@ -3500,6 +3508,7 @@ impl ChatComposer {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled,
|
||||
connectors_enabled,
|
||||
plugins_command_enabled,
|
||||
fast_command_enabled,
|
||||
personality_command_enabled,
|
||||
realtime_conversation_enabled,
|
||||
|
||||
@@ -38,6 +38,7 @@ pub(crate) struct CommandPopup {
|
||||
pub(crate) struct CommandPopupFlags {
|
||||
pub(crate) collaboration_modes_enabled: bool,
|
||||
pub(crate) connectors_enabled: bool,
|
||||
pub(crate) plugins_command_enabled: bool,
|
||||
pub(crate) fast_command_enabled: bool,
|
||||
pub(crate) personality_command_enabled: bool,
|
||||
pub(crate) realtime_conversation_enabled: bool,
|
||||
@@ -50,6 +51,7 @@ impl From<CommandPopupFlags> for slash_commands::BuiltinCommandFlags {
|
||||
Self {
|
||||
collaboration_modes_enabled: value.collaboration_modes_enabled,
|
||||
connectors_enabled: value.connectors_enabled,
|
||||
plugins_command_enabled: value.plugins_command_enabled,
|
||||
fast_command_enabled: value.fast_command_enabled,
|
||||
personality_command_enabled: value.personality_command_enabled,
|
||||
realtime_conversation_enabled: value.realtime_conversation_enabled,
|
||||
@@ -510,6 +512,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -532,6 +535,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -554,6 +558,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: false,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -584,6 +589,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: false,
|
||||
@@ -606,6 +612,7 @@ mod tests {
|
||||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: false,
|
||||
connectors_enabled: false,
|
||||
plugins_command_enabled: false,
|
||||
fast_command_enabled: false,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: true,
|
||||
|
||||
@@ -262,6 +262,11 @@ impl BottomPane {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub fn set_plugins_command_enabled(&mut self, enabled: bool) {
|
||||
self.composer.set_plugins_command_enabled(enabled);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub fn take_mention_bindings(&mut self) -> Vec<MentionBinding> {
|
||||
self.composer.take_mention_bindings()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::slash_command::built_in_slash_commands;
|
||||
pub(crate) struct BuiltinCommandFlags {
|
||||
pub(crate) collaboration_modes_enabled: bool,
|
||||
pub(crate) connectors_enabled: bool,
|
||||
pub(crate) plugins_command_enabled: bool,
|
||||
pub(crate) fast_command_enabled: bool,
|
||||
pub(crate) personality_command_enabled: bool,
|
||||
pub(crate) realtime_conversation_enabled: bool,
|
||||
@@ -31,6 +32,7 @@ pub(crate) fn builtins_for_input(flags: BuiltinCommandFlags) -> Vec<(&'static st
|
||||
|| !matches!(*cmd, SlashCommand::Collab | SlashCommand::Plan)
|
||||
})
|
||||
.filter(|(_, cmd)| flags.connectors_enabled || *cmd != SlashCommand::Apps)
|
||||
.filter(|(_, cmd)| flags.plugins_command_enabled || *cmd != SlashCommand::Plugins)
|
||||
.filter(|(_, cmd)| flags.fast_command_enabled || *cmd != SlashCommand::Fast)
|
||||
.filter(|(_, cmd)| flags.personality_command_enabled || *cmd != SlashCommand::Personality)
|
||||
.filter(|(_, cmd)| flags.realtime_conversation_enabled || *cmd != SlashCommand::Realtime)
|
||||
@@ -63,6 +65,7 @@ mod tests {
|
||||
BuiltinCommandFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
connectors_enabled: true,
|
||||
plugins_command_enabled: true,
|
||||
fast_command_enabled: true,
|
||||
personality_command_enabled: true,
|
||||
realtime_conversation_enabled: true,
|
||||
|
||||
@@ -1791,6 +1791,7 @@ impl ChatWidget {
|
||||
self.refresh_model_display();
|
||||
self.sync_fast_command_enabled();
|
||||
self.sync_personality_command_enabled();
|
||||
self.sync_plugins_command_enabled();
|
||||
self.refresh_plugin_mentions();
|
||||
let startup_tooltip_override = self.startup_tooltip_override.take();
|
||||
let show_fast_status = self.should_show_fast_status(&model_for_header, event.service_tier);
|
||||
@@ -4290,6 +4291,7 @@ impl ChatWidget {
|
||||
.set_collaboration_modes_enabled(/*enabled*/ true);
|
||||
widget.sync_fast_command_enabled();
|
||||
widget.sync_personality_command_enabled();
|
||||
widget.sync_plugins_command_enabled();
|
||||
widget
|
||||
.bottom_pane
|
||||
.set_queued_message_edit_binding(widget.queued_message_edit_binding);
|
||||
@@ -6069,6 +6071,7 @@ impl ChatWidget {
|
||||
| ServerNotification::ContextCompacted(_)
|
||||
| ServerNotification::FuzzyFileSearchSessionUpdated(_)
|
||||
| ServerNotification::FuzzyFileSearchSessionCompleted(_)
|
||||
| ServerNotification::ThreadRealtimeTranscriptUpdated(_)
|
||||
| ServerNotification::WindowsWorldWritableWarning(_)
|
||||
| ServerNotification::WindowsSandboxSetupCompleted(_)
|
||||
| ServerNotification::AccountLoginCompleted(_) => {}
|
||||
@@ -9002,6 +9005,7 @@ impl ChatWidget {
|
||||
self.sync_personality_command_enabled();
|
||||
}
|
||||
if feature == Feature::Plugins {
|
||||
self.sync_plugins_command_enabled();
|
||||
self.refresh_plugin_mentions();
|
||||
}
|
||||
if feature == Feature::PreventIdleSleep {
|
||||
@@ -9232,6 +9236,11 @@ impl ChatWidget {
|
||||
.set_personality_command_enabled(self.config.features.enabled(Feature::Personality));
|
||||
}
|
||||
|
||||
fn sync_plugins_command_enabled(&mut self) {
|
||||
self.bottom_pane
|
||||
.set_plugins_command_enabled(self.config.features.enabled(Feature::Plugins));
|
||||
}
|
||||
|
||||
fn current_model_supports_personality(&self) -> bool {
|
||||
let model = self.current_model();
|
||||
self.model_catalog
|
||||
|
||||
Reference in New Issue
Block a user