open-codex-ui 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- open_codex_ui-0.1.4.dist-info/METADATA +249 -0
- open_codex_ui-0.1.4.dist-info/RECORD +40 -0
- open_codex_ui-0.1.4.dist-info/WHEEL +5 -0
- open_codex_ui-0.1.4.dist-info/entry_points.txt +7 -0
- open_codex_ui-0.1.4.dist-info/top_level.txt +1 -0
- yier_web/__init__.py +6 -0
- yier_web/app.py +223 -0
- yier_web/auth.py +269 -0
- yier_web/cli.py +172 -0
- yier_web/codex/__init__.py +3 -0
- yier_web/codex/ipc_manager.py +1761 -0
- yier_web/codex/session_events.py +110 -0
- yier_web/codex/ws_commands.py +299 -0
- yier_web/config.py +651 -0
- yier_web/directory_picker.py +93 -0
- yier_web/event_stream.py +29 -0
- yier_web/frontend.py +204 -0
- yier_web/routes/__init__.py +17 -0
- yier_web/routes/codex.py +573 -0
- yier_web/routes/core.py +281 -0
- yier_web/schemas.py +534 -0
- yier_web/static/assets/CodexEmbedView-CN_-Mhe2.js +1 -0
- yier_web/static/assets/CodexView-wpI61iXa.js +606 -0
- yier_web/static/assets/LoginView-CELCom2O.js +101 -0
- yier_web/static/assets/api-CeihACIV.js +1099 -0
- yier_web/static/assets/index-BdFqJ-Kl.css +1 -0
- yier_web/static/assets/index-CjVNk6ja.js +183 -0
- yier_web/static/assets/index-mSBvq1p8.js +79 -0
- yier_web/static/assets/open-codex-ui-icon-DKJ1ZKj4.svg +99 -0
- yier_web/static/assets/primeicons-C6QP2o4f.woff2 +0 -0
- yier_web/static/assets/primeicons-DMOk5skT.eot +0 -0
- yier_web/static/assets/primeicons-Dr5RGzOO.svg +345 -0
- yier_web/static/assets/primeicons-MpK4pl85.ttf +0 -0
- yier_web/static/assets/primeicons-WjwUDZjB.woff +0 -0
- yier_web/static/assets/useCodexWorkspace-6QenDzvb.css +1 -0
- yier_web/static/assets/useCodexWorkspace-D1rCOO1x.js +1128 -0
- yier_web/static/brand/open-codex-ui-logo.svg +85 -0
- yier_web/static/favicon.ico +0 -0
- yier_web/static/favicon.svg +99 -0
- yier_web/static/index.html +24 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from collections.abc import Awaitable, Callable
|
|
5
|
+
import inspect
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
JsonDict = dict[str, Any]
|
|
10
|
+
CodexSessionEvent = JsonDict
|
|
11
|
+
CodexSessionEventQueue = asyncio.Queue[CodexSessionEvent]
|
|
12
|
+
CodexSessionEventSink = Callable[[CodexSessionEvent], Awaitable[None] | None]
|
|
13
|
+
Unsubscribe = Callable[[], None]
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CodexSessionEventHub:
|
|
19
|
+
"""Fan out Codex session events to thread subscribers and channel sinks."""
|
|
20
|
+
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
self._thread_subscribers: dict[str, set[CodexSessionEventQueue]] = {}
|
|
23
|
+
self._sinks: set[CodexSessionEventSink] = set()
|
|
24
|
+
|
|
25
|
+
def subscribe_thread(
|
|
26
|
+
self,
|
|
27
|
+
thread_id: str,
|
|
28
|
+
queue: CodexSessionEventQueue,
|
|
29
|
+
) -> bool:
|
|
30
|
+
subscribers = self._thread_subscribers.setdefault(thread_id, set())
|
|
31
|
+
was_empty = not subscribers
|
|
32
|
+
subscribers.add(queue)
|
|
33
|
+
return was_empty
|
|
34
|
+
|
|
35
|
+
def unsubscribe_thread(
|
|
36
|
+
self,
|
|
37
|
+
thread_id: str,
|
|
38
|
+
queue: CodexSessionEventQueue,
|
|
39
|
+
) -> bool:
|
|
40
|
+
subscribers = self._thread_subscribers.get(thread_id)
|
|
41
|
+
if subscribers is None or queue not in subscribers:
|
|
42
|
+
return False
|
|
43
|
+
subscribers.discard(queue)
|
|
44
|
+
if not subscribers:
|
|
45
|
+
self._thread_subscribers.pop(thread_id, None)
|
|
46
|
+
return True
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
def clear_thread(self, thread_id: str) -> None:
|
|
50
|
+
self._thread_subscribers.pop(thread_id, None)
|
|
51
|
+
|
|
52
|
+
def clear_thread_subscribers(self) -> None:
|
|
53
|
+
self._thread_subscribers.clear()
|
|
54
|
+
|
|
55
|
+
def clear(self) -> None:
|
|
56
|
+
self._thread_subscribers.clear()
|
|
57
|
+
self._sinks.clear()
|
|
58
|
+
|
|
59
|
+
def add_sink(self, sink: CodexSessionEventSink) -> Unsubscribe:
|
|
60
|
+
self._sinks.add(sink)
|
|
61
|
+
|
|
62
|
+
def unsubscribe() -> None:
|
|
63
|
+
self._sinks.discard(sink)
|
|
64
|
+
|
|
65
|
+
return unsubscribe
|
|
66
|
+
|
|
67
|
+
async def publish_thread_event(
|
|
68
|
+
self,
|
|
69
|
+
thread_id: str,
|
|
70
|
+
event: CodexSessionEvent,
|
|
71
|
+
) -> None:
|
|
72
|
+
for queue in list(self._thread_subscribers.get(thread_id, set())):
|
|
73
|
+
queue.put_nowait(event)
|
|
74
|
+
for sink in list(self._sinks):
|
|
75
|
+
await self._publish_to_sink(sink, event)
|
|
76
|
+
|
|
77
|
+
async def publish_to_thread_subscribers(
|
|
78
|
+
self,
|
|
79
|
+
thread_id: str,
|
|
80
|
+
event: CodexSessionEvent,
|
|
81
|
+
) -> None:
|
|
82
|
+
for queue in list(self._thread_subscribers.get(thread_id, set())):
|
|
83
|
+
queue.put_nowait(event)
|
|
84
|
+
|
|
85
|
+
async def publish_to_all_thread_subscribers(
|
|
86
|
+
self,
|
|
87
|
+
event: CodexSessionEvent,
|
|
88
|
+
) -> None:
|
|
89
|
+
for subscribers in list(self._thread_subscribers.values()):
|
|
90
|
+
for queue in list(subscribers):
|
|
91
|
+
queue.put_nowait(event)
|
|
92
|
+
|
|
93
|
+
async def publish_global_event(self, event: CodexSessionEvent) -> None:
|
|
94
|
+
for subscribers in list(self._thread_subscribers.values()):
|
|
95
|
+
for queue in list(subscribers):
|
|
96
|
+
queue.put_nowait(event)
|
|
97
|
+
for sink in list(self._sinks):
|
|
98
|
+
await self._publish_to_sink(sink, event)
|
|
99
|
+
|
|
100
|
+
async def _publish_to_sink(
|
|
101
|
+
self,
|
|
102
|
+
sink: CodexSessionEventSink,
|
|
103
|
+
event: CodexSessionEvent,
|
|
104
|
+
) -> None:
|
|
105
|
+
try:
|
|
106
|
+
result = sink(event)
|
|
107
|
+
if inspect.isawaitable(result):
|
|
108
|
+
await result
|
|
109
|
+
except Exception as exc:
|
|
110
|
+
logger.warning("Codex session event sink failed: %s", exc)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from yier_web.codex.ipc_manager import CodexIpcManager, CodexSubscriberQueue
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _payload_text(payload: dict[str, Any], key: str) -> str:
|
|
10
|
+
value = payload.get(key)
|
|
11
|
+
return value.strip() if isinstance(value, str) else ""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _required_payload_text(payload: dict[str, Any], key: str) -> str:
|
|
15
|
+
value = _payload_text(payload, key)
|
|
16
|
+
if not value:
|
|
17
|
+
raise ValueError(f"{key} is required.")
|
|
18
|
+
return value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _payload_dict(payload: dict[str, Any], key: str) -> dict[str, Any] | None:
|
|
22
|
+
value = payload.get(key)
|
|
23
|
+
return dict(value) if isinstance(value, dict) else None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _payload_dict_list(payload: dict[str, Any], key: str) -> list[dict[str, Any]]:
|
|
27
|
+
value = payload.get(key)
|
|
28
|
+
if not isinstance(value, list):
|
|
29
|
+
return []
|
|
30
|
+
return [dict(item) for item in value if isinstance(item, dict)]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _payload_int(payload: dict[str, Any], key: str) -> int | None:
|
|
34
|
+
value = payload.get(key)
|
|
35
|
+
if isinstance(value, bool):
|
|
36
|
+
return None
|
|
37
|
+
return value if isinstance(value, int) else None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(slots=True)
|
|
41
|
+
class CodexWsCommandContext:
|
|
42
|
+
manager: CodexIpcManager
|
|
43
|
+
payload: dict[str, Any]
|
|
44
|
+
outbox: CodexSubscriberQueue
|
|
45
|
+
subscribed_thread_ids: set[str]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class CodexWsCommandStrategy:
|
|
49
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def _publish_workspace(
|
|
54
|
+
manager: CodexIpcManager,
|
|
55
|
+
outbox: CodexSubscriberQueue,
|
|
56
|
+
) -> dict[str, Any]:
|
|
57
|
+
workspace = await manager.workspace()
|
|
58
|
+
payload = workspace.model_dump(mode="json")
|
|
59
|
+
await outbox.put(
|
|
60
|
+
{
|
|
61
|
+
"type": "workspace",
|
|
62
|
+
"payload": payload,
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
return payload
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ListThreadsCommandStrategy(CodexWsCommandStrategy):
|
|
69
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
70
|
+
return await _publish_workspace(context.manager, context.outbox)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class StartThreadCommandStrategy(CodexWsCommandStrategy):
|
|
74
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
75
|
+
result = await context.manager.start_thread(
|
|
76
|
+
project_path=_payload_text(context.payload, "project_path") or None,
|
|
77
|
+
host_id=_payload_text(context.payload, "host_id") or "local",
|
|
78
|
+
)
|
|
79
|
+
await _publish_workspace(context.manager, context.outbox)
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ThreadCommandStrategy(CodexWsCommandStrategy):
|
|
84
|
+
def thread_id(self, context: CodexWsCommandContext) -> str:
|
|
85
|
+
return _required_payload_text(context.payload, "thread_id")
|
|
86
|
+
|
|
87
|
+
def host_id(self, context: CodexWsCommandContext) -> str | None:
|
|
88
|
+
return _payload_text(context.payload, "host_id") or None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class SubscribeThreadCommandStrategy(ThreadCommandStrategy):
|
|
92
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
93
|
+
thread_id = self.thread_id(context)
|
|
94
|
+
state = await context.manager.subscribe(
|
|
95
|
+
thread_id,
|
|
96
|
+
context.outbox,
|
|
97
|
+
host_id=self.host_id(context),
|
|
98
|
+
)
|
|
99
|
+
context.subscribed_thread_ids.add(thread_id)
|
|
100
|
+
return {"thread_id": thread_id, "state": state}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class UnsubscribeThreadCommandStrategy(ThreadCommandStrategy):
|
|
104
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
105
|
+
thread_id = self.thread_id(context)
|
|
106
|
+
await context.manager.unsubscribe(thread_id, context.outbox)
|
|
107
|
+
context.subscribed_thread_ids.discard(thread_id)
|
|
108
|
+
return {"thread_id": thread_id}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class SendPromptCommandStrategy(ThreadCommandStrategy):
|
|
112
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
113
|
+
thread_id = self.thread_id(context)
|
|
114
|
+
prompt = _payload_text(context.payload, "prompt")
|
|
115
|
+
attachments = _payload_dict_list(context.payload, "attachments")
|
|
116
|
+
if not prompt and not attachments:
|
|
117
|
+
raise ValueError("prompt or attachments is required.")
|
|
118
|
+
await context.manager.send_prompt(
|
|
119
|
+
thread_id,
|
|
120
|
+
prompt,
|
|
121
|
+
collaboration_mode=_payload_dict(context.payload, "collaboration_mode"),
|
|
122
|
+
attachments=attachments,
|
|
123
|
+
approval_policy=_payload_text(context.payload, "approval_policy") or None,
|
|
124
|
+
approvals_reviewer=_payload_text(context.payload, "approvals_reviewer")
|
|
125
|
+
or None,
|
|
126
|
+
sandbox_policy=_payload_dict(context.payload, "sandbox_policy"),
|
|
127
|
+
)
|
|
128
|
+
return {"thread_id": thread_id}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SteerPromptCommandStrategy(ThreadCommandStrategy):
|
|
132
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
133
|
+
thread_id = self.thread_id(context)
|
|
134
|
+
prompt = _required_payload_text(context.payload, "prompt")
|
|
135
|
+
await context.manager.steer_prompt(thread_id, prompt)
|
|
136
|
+
return {"thread_id": thread_id}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class EnqueueFollowupCommandStrategy(ThreadCommandStrategy):
|
|
140
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
141
|
+
thread_id = self.thread_id(context)
|
|
142
|
+
prompt = _required_payload_text(context.payload, "prompt")
|
|
143
|
+
return await context.manager.enqueue_followup(thread_id, prompt)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class RemoveFollowupCommandStrategy(ThreadCommandStrategy):
|
|
147
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
148
|
+
thread_id = self.thread_id(context)
|
|
149
|
+
message_id = _required_payload_text(context.payload, "message_id")
|
|
150
|
+
await context.manager.remove_followup(thread_id, message_id)
|
|
151
|
+
return {"thread_id": thread_id, "message_id": message_id}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class InterruptTurnCommandStrategy(ThreadCommandStrategy):
|
|
155
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
156
|
+
thread_id = self.thread_id(context)
|
|
157
|
+
turn_id = _payload_text(context.payload, "turn_id") or None
|
|
158
|
+
interrupted = await context.manager.interrupt_turn(thread_id, turn_id)
|
|
159
|
+
return {"thread_id": thread_id, "forwarded": interrupted}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class CompactThreadCommandStrategy(ThreadCommandStrategy):
|
|
163
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
164
|
+
thread_id = self.thread_id(context)
|
|
165
|
+
forwarded = await context.manager.compact_thread(thread_id)
|
|
166
|
+
return {"thread_id": thread_id, "forwarded": forwarded}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class SetThreadGoalCommandStrategy(ThreadCommandStrategy):
|
|
170
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
171
|
+
thread_id = self.thread_id(context)
|
|
172
|
+
response = await context.manager.set_thread_goal(
|
|
173
|
+
thread_id,
|
|
174
|
+
objective=_payload_text(context.payload, "objective") or None,
|
|
175
|
+
status=_payload_text(context.payload, "status") or None,
|
|
176
|
+
token_budget=_payload_int(context.payload, "token_budget")
|
|
177
|
+
or _payload_int(context.payload, "tokenBudget"),
|
|
178
|
+
)
|
|
179
|
+
return {"thread_id": thread_id, **response}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class GetThreadGoalCommandStrategy(ThreadCommandStrategy):
|
|
183
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
184
|
+
thread_id = self.thread_id(context)
|
|
185
|
+
goal = await context.manager.get_thread_goal(thread_id)
|
|
186
|
+
return {"thread_id": thread_id, "goal": goal}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class ClearThreadGoalCommandStrategy(ThreadCommandStrategy):
|
|
190
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
191
|
+
thread_id = self.thread_id(context)
|
|
192
|
+
response = await context.manager.clear_thread_goal(thread_id)
|
|
193
|
+
return {"thread_id": thread_id, **response}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class SetCollaborationModeCommandStrategy(ThreadCommandStrategy):
|
|
197
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
198
|
+
thread_id = self.thread_id(context)
|
|
199
|
+
await context.manager.set_collaboration_mode(
|
|
200
|
+
thread_id,
|
|
201
|
+
_payload_dict(context.payload, "collaboration_mode"),
|
|
202
|
+
)
|
|
203
|
+
return {"thread_id": thread_id}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class SubmitUserInputResponseCommandStrategy(ThreadCommandStrategy):
|
|
207
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
208
|
+
thread_id = self.thread_id(context)
|
|
209
|
+
request_id = _required_payload_text(context.payload, "request_id")
|
|
210
|
+
submitted = await context.manager.submit_user_input_response(
|
|
211
|
+
thread_id,
|
|
212
|
+
request_id,
|
|
213
|
+
_payload_dict(context.payload, "response") or {"answers": {}},
|
|
214
|
+
)
|
|
215
|
+
return {"thread_id": thread_id, "submitted": submitted}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class RenameThreadCommandStrategy(ThreadCommandStrategy):
|
|
219
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
220
|
+
thread_id = self.thread_id(context)
|
|
221
|
+
name = _required_payload_text(context.payload, "name")
|
|
222
|
+
await context.manager.rename_thread(thread_id, name)
|
|
223
|
+
return {"thread_id": thread_id}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ArchiveThreadCommandStrategy(ThreadCommandStrategy):
|
|
227
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
228
|
+
thread_id = self.thread_id(context)
|
|
229
|
+
await context.manager.archive_thread(thread_id)
|
|
230
|
+
return {"thread_id": thread_id}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class ForkThreadCommandStrategy(ThreadCommandStrategy):
|
|
234
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
235
|
+
thread_id = self.thread_id(context)
|
|
236
|
+
result = await context.manager.fork_thread(
|
|
237
|
+
thread_id,
|
|
238
|
+
host_id=self.host_id(context),
|
|
239
|
+
)
|
|
240
|
+
await _publish_workspace(context.manager, context.outbox)
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class UnarchiveThreadCommandStrategy(ThreadCommandStrategy):
|
|
245
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
246
|
+
thread_id = self.thread_id(context)
|
|
247
|
+
await context.manager.unarchive_thread(
|
|
248
|
+
thread_id,
|
|
249
|
+
host_id=self.host_id(context),
|
|
250
|
+
)
|
|
251
|
+
return {"thread_id": thread_id}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class ListSkillsCommandStrategy(CodexWsCommandStrategy):
|
|
255
|
+
async def execute(self, context: CodexWsCommandContext) -> dict[str, Any]:
|
|
256
|
+
thread_id = _payload_text(context.payload, "thread_id") or None
|
|
257
|
+
cwd = _payload_text(context.payload, "cwd") or None
|
|
258
|
+
force_reload = bool(
|
|
259
|
+
context.payload.get("force_reload") or context.payload.get("forceReload")
|
|
260
|
+
)
|
|
261
|
+
skills = await context.manager.list_skills(
|
|
262
|
+
thread_id=thread_id,
|
|
263
|
+
host_id=_payload_text(context.payload, "host_id") or None,
|
|
264
|
+
cwd=cwd,
|
|
265
|
+
force_reload=force_reload,
|
|
266
|
+
)
|
|
267
|
+
return {"skills": skills}
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class CodexWsCommandStrategyFactory:
|
|
271
|
+
def __init__(self) -> None:
|
|
272
|
+
self.strategies: dict[str, CodexWsCommandStrategy] = {
|
|
273
|
+
"list_threads": ListThreadsCommandStrategy(),
|
|
274
|
+
"start_thread": StartThreadCommandStrategy(),
|
|
275
|
+
"subscribe_thread": SubscribeThreadCommandStrategy(),
|
|
276
|
+
"unsubscribe_thread": UnsubscribeThreadCommandStrategy(),
|
|
277
|
+
"send_prompt": SendPromptCommandStrategy(),
|
|
278
|
+
"steer_prompt": SteerPromptCommandStrategy(),
|
|
279
|
+
"enqueue_followup": EnqueueFollowupCommandStrategy(),
|
|
280
|
+
"remove_followup": RemoveFollowupCommandStrategy(),
|
|
281
|
+
"interrupt_turn": InterruptTurnCommandStrategy(),
|
|
282
|
+
"compact_thread": CompactThreadCommandStrategy(),
|
|
283
|
+
"set_thread_goal": SetThreadGoalCommandStrategy(),
|
|
284
|
+
"get_thread_goal": GetThreadGoalCommandStrategy(),
|
|
285
|
+
"clear_thread_goal": ClearThreadGoalCommandStrategy(),
|
|
286
|
+
"set_collaboration_mode": SetCollaborationModeCommandStrategy(),
|
|
287
|
+
"submit_user_input_response": SubmitUserInputResponseCommandStrategy(),
|
|
288
|
+
"rename_thread": RenameThreadCommandStrategy(),
|
|
289
|
+
"archive_thread": ArchiveThreadCommandStrategy(),
|
|
290
|
+
"fork_thread": ForkThreadCommandStrategy(),
|
|
291
|
+
"unarchive_thread": UnarchiveThreadCommandStrategy(),
|
|
292
|
+
"list_skills": ListSkillsCommandStrategy(),
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
def get(self, message_type: str) -> CodexWsCommandStrategy:
|
|
296
|
+
strategy = self.strategies.get(message_type)
|
|
297
|
+
if strategy is None:
|
|
298
|
+
raise ValueError(f"Unsupported Codex command: {message_type}")
|
|
299
|
+
return strategy
|