Skip to content

Commit 188788e

Browse files
committed
Restore built-in command options
1 parent 5d3fcf5 commit 188788e

File tree

19 files changed

+623
-103
lines changed

19 files changed

+623
-103
lines changed

src-tauri/src/bin/codex_monitor_daemon.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,10 @@ impl DaemonState {
677677
codex_core::list_mcp_server_status_core(&self.sessions, workspace_id, cursor, limit).await
678678
}
679679

680+
async fn list_slash_commands(&self, workspace_id: String) -> Result<Value, String> {
681+
codex_core::list_slash_commands_core(&self.sessions, workspace_id).await
682+
}
683+
680684
async fn archive_thread(
681685
&self,
682686
workspace_id: String,
@@ -689,8 +693,26 @@ impl DaemonState {
689693
&self,
690694
workspace_id: String,
691695
thread_id: String,
696+
model: Option<String>,
697+
) -> Result<Value, String> {
698+
codex_core::compact_thread_core(&self.sessions, workspace_id, thread_id, model).await
699+
}
700+
701+
async fn execute_slash_command(
702+
&self,
703+
workspace_id: String,
704+
thread_id: String,
705+
command: String,
706+
arguments: Option<String>,
692707
) -> Result<Value, String> {
693-
codex_core::compact_thread_core(&self.sessions, workspace_id, thread_id).await
708+
codex_core::execute_slash_command_core(
709+
&self.sessions,
710+
workspace_id,
711+
thread_id,
712+
command,
713+
arguments,
714+
)
715+
.await
694716
}
695717

696718
async fn set_thread_name(

src-tauri/src/bin/codex_monitor_daemon/rpc/codex.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ pub(super) async fn try_handle(
7676
.await,
7777
)
7878
}
79+
"list_slash_commands" => {
80+
let workspace_id = match parse_string(params, "workspaceId") {
81+
Ok(value) => value,
82+
Err(err) => return Some(Err(err)),
83+
};
84+
Some(state.list_slash_commands(workspace_id).await)
85+
}
7986
"archive_thread" => {
8087
let workspace_id = match parse_string(params, "workspaceId") {
8188
Ok(value) => value,
@@ -96,7 +103,28 @@ pub(super) async fn try_handle(
96103
Ok(value) => value,
97104
Err(err) => return Some(Err(err)),
98105
};
99-
Some(state.compact_thread(workspace_id, thread_id).await)
106+
let model = parse_optional_string(params, "model");
107+
Some(state.compact_thread(workspace_id, thread_id, model).await)
108+
}
109+
"execute_slash_command" => {
110+
let workspace_id = match parse_string(params, "workspaceId") {
111+
Ok(value) => value,
112+
Err(err) => return Some(Err(err)),
113+
};
114+
let thread_id = match parse_string(params, "threadId") {
115+
Ok(value) => value,
116+
Err(err) => return Some(Err(err)),
117+
};
118+
let command = match parse_string(params, "command") {
119+
Ok(value) => value,
120+
Err(err) => return Some(Err(err)),
121+
};
122+
let arguments = parse_optional_string(params, "arguments");
123+
Some(
124+
state
125+
.execute_slash_command(workspace_id, thread_id, command, arguments)
126+
.await,
127+
)
100128
}
101129
"set_thread_name" => {
102130
let workspace_id = match parse_string(params, "workspaceId") {

src-tauri/src/codex/mod.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,25 @@ pub(crate) async fn list_mcp_server_status(
173173
codex_core::list_mcp_server_status_core(&state.sessions, workspace_id, cursor, limit).await
174174
}
175175

176+
#[tauri::command]
177+
pub(crate) async fn list_slash_commands(
178+
workspace_id: String,
179+
state: State<'_, AppState>,
180+
app: AppHandle,
181+
) -> Result<Value, String> {
182+
if remote_backend::is_remote_mode(&*state).await {
183+
return remote_backend::call_remote(
184+
&*state,
185+
app,
186+
"list_slash_commands",
187+
json!({ "workspaceId": workspace_id }),
188+
)
189+
.await;
190+
}
191+
192+
codex_core::list_slash_commands_core(&state.sessions, workspace_id).await
193+
}
194+
176195
#[tauri::command]
177196
pub(crate) async fn archive_thread(
178197
workspace_id: String,
@@ -197,6 +216,7 @@ pub(crate) async fn archive_thread(
197216
pub(crate) async fn compact_thread(
198217
workspace_id: String,
199218
thread_id: String,
219+
model: Option<String>,
200220
state: State<'_, AppState>,
201221
app: AppHandle,
202222
) -> Result<Value, String> {
@@ -205,12 +225,46 @@ pub(crate) async fn compact_thread(
205225
&*state,
206226
app,
207227
"compact_thread",
208-
json!({ "workspaceId": workspace_id, "threadId": thread_id }),
228+
json!({ "workspaceId": workspace_id, "threadId": thread_id, "model": model }),
209229
)
210230
.await;
211231
}
212232

213-
codex_core::compact_thread_core(&state.sessions, workspace_id, thread_id).await
233+
codex_core::compact_thread_core(&state.sessions, workspace_id, thread_id, model).await
234+
}
235+
236+
#[tauri::command]
237+
pub(crate) async fn execute_slash_command(
238+
workspace_id: String,
239+
thread_id: String,
240+
command: String,
241+
arguments: Option<String>,
242+
state: State<'_, AppState>,
243+
app: AppHandle,
244+
) -> Result<Value, String> {
245+
if remote_backend::is_remote_mode(&*state).await {
246+
return remote_backend::call_remote(
247+
&*state,
248+
app,
249+
"execute_slash_command",
250+
json!({
251+
"workspaceId": workspace_id,
252+
"threadId": thread_id,
253+
"command": command,
254+
"arguments": arguments,
255+
}),
256+
)
257+
.await;
258+
}
259+
260+
codex_core::execute_slash_command_core(
261+
&state.sessions,
262+
workspace_id,
263+
thread_id,
264+
command,
265+
arguments,
266+
)
267+
.await
214268
}
215269

216270
#[tauri::command]

src-tauri/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,10 @@ pub fn run() {
240240
codex::fork_thread,
241241
codex::list_threads,
242242
codex::list_mcp_server_status,
243+
codex::list_slash_commands,
243244
codex::archive_thread,
244245
codex::compact_thread,
246+
codex::execute_slash_command,
245247
codex::set_thread_name,
246248
codex::collaboration_mode_list,
247249
workspaces::connect_workspace,

src-tauri/src/shared/codex_core.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,16 @@ pub(crate) async fn list_mcp_server_status_core(
805805
Ok(json!({ "result": { "data": data, "nextCursor": null } }))
806806
}
807807

808+
pub(crate) async fn list_slash_commands_core(
809+
sessions: &Mutex<HashMap<String, Arc<WorkspaceSession>>>,
810+
workspace_id: String,
811+
) -> Result<Value, String> {
812+
let session = get_session_clone(sessions, &workspace_id).await?;
813+
let response = session.rest_get("/command").await?;
814+
let data = response.as_array().cloned().unwrap_or_default();
815+
Ok(json!({ "result": { "data": data } }))
816+
}
817+
808818
pub(crate) async fn archive_thread_core(
809819
sessions: &Mutex<HashMap<String, Arc<WorkspaceSession>>>,
810820
workspace_id: String,
@@ -822,12 +832,58 @@ pub(crate) async fn compact_thread_core(
822832
sessions: &Mutex<HashMap<String, Arc<WorkspaceSession>>>,
823833
workspace_id: String,
824834
thread_id: String,
835+
model: Option<String>,
825836
) -> Result<Value, String> {
826837
let session = get_session_clone(sessions, &workspace_id).await?;
827-
// Send /compact as a message via POST /session/:id/message.
828-
let path = format!("/session/{thread_id}/message");
838+
let requested_model = normalize_optional_string(model);
839+
let model_override = if let Some(ref model_id) = requested_model {
840+
resolve_prompt_model_override(session.as_ref(), model_id)
841+
.await
842+
.ok_or_else(|| format!("Failed to resolve model `{model_id}` for context compaction."))?
843+
} else {
844+
return Err(
845+
"No model selected for context compaction. Select a model (or connect a provider) and try again."
846+
.to_string(),
847+
);
848+
};
849+
850+
let provider_id = model_override
851+
.get("providerID")
852+
.and_then(|v| v.as_str())
853+
.ok_or_else(|| "Compaction model resolution missing providerID.".to_string())?;
854+
let model_id = model_override
855+
.get("modelID")
856+
.and_then(|v| v.as_str())
857+
.ok_or_else(|| "Compaction model resolution missing modelID.".to_string())?;
858+
859+
let path = format!("/session/{thread_id}/summarize");
860+
session
861+
.rest_post(
862+
&path,
863+
json!({
864+
"providerID": provider_id,
865+
"modelID": model_id,
866+
}),
867+
)
868+
.await
869+
}
870+
871+
pub(crate) async fn execute_slash_command_core(
872+
sessions: &Mutex<HashMap<String, Arc<WorkspaceSession>>>,
873+
workspace_id: String,
874+
thread_id: String,
875+
command: String,
876+
arguments: Option<String>,
877+
) -> Result<Value, String> {
878+
let command = command.trim().trim_start_matches('/').to_string();
879+
if command.is_empty() {
880+
return Err("empty slash command".to_string());
881+
}
882+
let session = get_session_clone(sessions, &workspace_id).await?;
883+
let path = format!("/session/{thread_id}/command");
829884
let body = json!({
830-
"parts": [{ "type": "text", "text": "/compact" }]
885+
"command": command,
886+
"arguments": arguments.unwrap_or_default().trim().to_string()
831887
});
832888
session.rest_post(&path, body).await
833889
}

src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,8 @@ function MainApp() {
536536
startApps,
537537
startMcp,
538538
startStatus,
539+
slashCommands,
540+
executeSlashCommand,
539541
reviewPrompt,
540542
closeReviewPrompt,
541543
showPresetStep,
@@ -1191,6 +1193,8 @@ function MainApp() {
11911193
startApps,
11921194
startMcp,
11931195
startStatus,
1196+
slashCommands,
1197+
executeSlashCommand,
11941198
});
11951199

11961200
const {
@@ -2089,6 +2093,7 @@ function MainApp() {
20892093
onSelectAccessMode: handleSelectAccessMode,
20902094
skills,
20912095
appsEnabled: appsFeatureEnabled,
2096+
slashCommands,
20922097
apps,
20932098
prompts,
20942099
files,
@@ -2189,6 +2194,7 @@ function MainApp() {
21892194
onSelectInstance={handleSelectWorkspaceInstance}
21902195
skills={skills}
21912196
appsEnabled={appsFeatureEnabled}
2197+
slashCommands={slashCommands}
21922198
apps={apps}
21932199
prompts={prompts}
21942200
files={files}

src/features/app/hooks/useComposerController.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { useCallback, useMemo, useState } from "react";
2-
import type { AppMention, QueuedMessage, WorkspaceInfo } from "../../../types";
2+
import type {
3+
AppMention,
4+
OpenCodeSlashCommand,
5+
QueuedMessage,
6+
WorkspaceInfo,
7+
} from "../../../types";
38
import { useComposerImages } from "../../composer/hooks/useComposerImages";
49
import { useQueuedSend } from "../../threads/hooks/useQueuedSend";
510

@@ -23,6 +28,8 @@ export function useComposerController({
2328
startApps,
2429
startMcp,
2530
startStatus,
31+
slashCommands = [],
32+
executeSlashCommand = async () => {},
2633
}: {
2734
activeThreadId: string | null;
2835
activeTurnId: string | null;
@@ -55,6 +62,8 @@ export function useComposerController({
5562
startApps: (text: string) => Promise<void>;
5663
startMcp: (text: string) => Promise<void>;
5764
startStatus: (text: string) => Promise<void>;
65+
slashCommands?: OpenCodeSlashCommand[];
66+
executeSlashCommand?: (text: string) => Promise<void>;
5867
}) {
5968
const [composerDraftsByThread, setComposerDraftsByThread] = useState<
6069
Record<string, string>
@@ -98,6 +107,8 @@ export function useComposerController({
98107
startApps,
99108
startMcp,
100109
startStatus,
110+
slashCommands,
111+
executeSlashCommand,
101112
clearActiveImages,
102113
});
103114

src/features/composer/components/Composer.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
ComposerEditorSettings,
1515
CustomPromptOption,
1616
DictationTranscript,
17+
OpenCodeSlashCommand,
1718
QueuedMessage,
1819
ThreadTokenUsage,
1920
} from "../../../types";
@@ -52,6 +53,7 @@ type ComposerProps = {
5253
disabled?: boolean;
5354
isConnected?: boolean;
5455
appsEnabled: boolean;
56+
slashCommands?: OpenCodeSlashCommand[];
5557
isProcessing: boolean;
5658
steerEnabled: boolean;
5759
collaborationModes: { id: string; label: string }[];
@@ -156,6 +158,7 @@ export const Composer = memo(function Composer({
156158
disabled = false,
157159
isConnected = false,
158160
appsEnabled,
161+
slashCommands = [],
159162
isProcessing,
160163
steerEnabled,
161164
collaborationModes,
@@ -283,6 +286,7 @@ export const Composer = memo(function Composer({
283286
selectionStart,
284287
disabled,
285288
appsEnabled,
289+
slashCommands,
286290
skills,
287291
apps,
288292
prompts,

0 commit comments

Comments
 (0)