@bitkyc08/opencodex 2.7.2 → 2.7.3
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.
|
@@ -37,4 +37,4 @@ v2: 多线程代理(spawn_agent)。所有模型使用 v2 协作界面。
|
|
|
37
37
|
-d '{
|
|
38
38
|
"model": "gpt-5.4",
|
|
39
39
|
"input": "Hello, world!"
|
|
40
|
-
}'`})]})]})}var Ft=`opencodex-api-token`,It=!1,Lt=null;function Rt(e){try{let t=e instanceof Request?e.url:String(e);return new URL(t,window.location.href).pathname}catch{return null}}function zt(e){let t=Rt(e);return!!t&&(t.startsWith(`/api/`)||t.startsWith(`/v1/`))}function Bt(){try{return sessionStorage.getItem(Ft)?.trim()||null}catch{return null}}function Vt(e){try{sessionStorage.setItem(Ft,e)}catch{}}function Ht(){try{sessionStorage.removeItem(Ft)}catch{}}function Ut(e,t,n){let r=new Headers(t?.headers??(e instanceof Request?e.headers:void 0));return r.set(`X-OpenCodex-API-Key`,n),e instanceof Request?[new Request(e,{headers:r}),t?{...t,headers:r}:void 0]:[e,{...t,headers:r}]}async function Wt(){return Lt||(Lt=Promise.resolve().then(()=>window.prompt(`OpenCodex API token`)?.trim()||null).finally(()=>{Lt=null}),Lt)}function Gt(){if(It)return;It=!0;let e=window.fetch.bind(window);window.fetch=async(t,n)=>{if(!zt(t))return e(t,n);let r=Bt(),[i,a]=r?Ut(t,n,r):[t,n],o=await e(i,a);if(o.status!==401)return o;r&&Ht();let s=await Wt();if(!s)return o;Vt(s);let[c,l]=Ut(t,n,s),u=await e(c,l);return u.status===401&&Ht(),u}}Gt();var Kt=new Set([`dashboard`,`providers`,`models`,`subagents`,`logs`,`debug`,`usage`,`codex-auth`,`api`]);function qt(){let e=location.hash.replace(/^#\/?/,``);return Kt.has(e)?e:`dashboard`}var Jt=``,Yt=`ocx-theme`,Xt=[{id:`dashboard`,tkey:`nav.dashboard`,Icon:w},{id:`providers`,tkey:`nav.providers`,Icon:T},{id:`models`,tkey:`nav.models`,Icon:E},{id:`subagents`,tkey:`nav.subagents`,Icon:ee},{id:`logs`,tkey:`nav.logs`,Icon:D},{id:`debug`,tkey:`nav.debug`,Icon:te},{id:`usage`,tkey:`nav.usage`,Icon:ne},{id:`codex-auth`,tkey:`nav.codexAuth`,Icon:L},{id:`api`,tkey:`nav.api`,Icon:me}],Zt={light:de,dark:fe,system:pe},Qt={light:`theme.light`,dark:`theme.dark`,system:`theme.system`};function $t(e){if(!e||typeof e!=`object`||!(`version`in e))return null;let t=e.version;return typeof t==`string`&&t.length>0?t:null}function en(){let e=localStorage.getItem(Yt);return e===`light`||e===`dark`?e:`system`}function tn(){let[e,t]=(0,_.useState)(qt),n=e=>{location.hash=e,t(e)},[r,i]=(0,_.useState)(en),[a,o]=(0,_.useState)(null),{locale:s,setLocale:c}=Ce(),l=we();(0,_.useEffect)(()=>{let e=()=>t(qt());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useEffect)(()=>{let e=document.documentElement;r===`system`?(e.removeAttribute(`data-theme`),localStorage.removeItem(Yt)):(e.setAttribute(`data-theme`,r),localStorage.setItem(Yt,r))},[r]),(0,_.useEffect)(()=>{let e=!1,t=async()=>{try{let t=await fetch(`${Jt}/healthz`);if(!t.ok)return;let n=$t(await t.json());!e&&n&&o(n)}catch{}};t();let n=setInterval(t,3e4);return()=>{e=!0,clearInterval(n)}},[]);let u=()=>i(e=>e===`light`?`dark`:e===`dark`?`system`:`light`),d=Zt[r],f=a??`2.7.
|
|
40
|
+
}'`})]})]})}var Ft=`opencodex-api-token`,It=!1,Lt=null;function Rt(e){try{let t=e instanceof Request?e.url:String(e);return new URL(t,window.location.href).pathname}catch{return null}}function zt(e){let t=Rt(e);return!!t&&(t.startsWith(`/api/`)||t.startsWith(`/v1/`))}function Bt(){try{return sessionStorage.getItem(Ft)?.trim()||null}catch{return null}}function Vt(e){try{sessionStorage.setItem(Ft,e)}catch{}}function Ht(){try{sessionStorage.removeItem(Ft)}catch{}}function Ut(e,t,n){let r=new Headers(t?.headers??(e instanceof Request?e.headers:void 0));return r.set(`X-OpenCodex-API-Key`,n),e instanceof Request?[new Request(e,{headers:r}),t?{...t,headers:r}:void 0]:[e,{...t,headers:r}]}async function Wt(){return Lt||(Lt=Promise.resolve().then(()=>window.prompt(`OpenCodex API token`)?.trim()||null).finally(()=>{Lt=null}),Lt)}function Gt(){if(It)return;It=!0;let e=window.fetch.bind(window);window.fetch=async(t,n)=>{if(!zt(t))return e(t,n);let r=Bt(),[i,a]=r?Ut(t,n,r):[t,n],o=await e(i,a);if(o.status!==401)return o;r&&Ht();let s=await Wt();if(!s)return o;Vt(s);let[c,l]=Ut(t,n,s),u=await e(c,l);return u.status===401&&Ht(),u}}Gt();var Kt=new Set([`dashboard`,`providers`,`models`,`subagents`,`logs`,`debug`,`usage`,`codex-auth`,`api`]);function qt(){let e=location.hash.replace(/^#\/?/,``);return Kt.has(e)?e:`dashboard`}var Jt=``,Yt=`ocx-theme`,Xt=[{id:`dashboard`,tkey:`nav.dashboard`,Icon:w},{id:`providers`,tkey:`nav.providers`,Icon:T},{id:`models`,tkey:`nav.models`,Icon:E},{id:`subagents`,tkey:`nav.subagents`,Icon:ee},{id:`logs`,tkey:`nav.logs`,Icon:D},{id:`debug`,tkey:`nav.debug`,Icon:te},{id:`usage`,tkey:`nav.usage`,Icon:ne},{id:`codex-auth`,tkey:`nav.codexAuth`,Icon:L},{id:`api`,tkey:`nav.api`,Icon:me}],Zt={light:de,dark:fe,system:pe},Qt={light:`theme.light`,dark:`theme.dark`,system:`theme.system`};function $t(e){if(!e||typeof e!=`object`||!(`version`in e))return null;let t=e.version;return typeof t==`string`&&t.length>0?t:null}function en(){let e=localStorage.getItem(Yt);return e===`light`||e===`dark`?e:`system`}function tn(){let[e,t]=(0,_.useState)(qt),n=e=>{location.hash=e,t(e)},[r,i]=(0,_.useState)(en),[a,o]=(0,_.useState)(null),{locale:s,setLocale:c}=Ce(),l=we();(0,_.useEffect)(()=>{let e=()=>t(qt());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]),(0,_.useEffect)(()=>{let e=document.documentElement;r===`system`?(e.removeAttribute(`data-theme`),localStorage.removeItem(Yt)):(e.setAttribute(`data-theme`,r),localStorage.setItem(Yt,r))},[r]),(0,_.useEffect)(()=>{let e=!1,t=async()=>{try{let t=await fetch(`${Jt}/healthz`);if(!t.ok)return;let n=$t(await t.json());!e&&n&&o(n)}catch{}};t();let n=setInterval(t,3e4);return()=>{e=!0,clearInterval(n)}},[]);let u=()=>i(e=>e===`light`?`dark`:e===`dark`?`system`:`light`),d=Zt[r],f=a??`2.7.3`,[p,m]=(0,_.useState)(!1);return(0,S.jsxs)(`div`,{className:`app`,children:[(0,S.jsxs)(`aside`,{className:`sidebar`,children:[(0,S.jsxs)(`div`,{className:`brand`,children:[(0,S.jsx)(`span`,{className:`brand-logo`,role:`img`,"aria-label":`opencodex logo`}),(0,S.jsx)(`span`,{className:`name`,children:`opencodex`}),(0,S.jsxs)(`span`,{className:`ver`,children:[`v`,f]})]}),(0,S.jsx)(`nav`,{children:Xt.map(({id:t,tkey:r,Icon:i})=>(0,S.jsxs)(`button`,{className:`nav-item${e===t?` active`:``}`,"data-page":t,onClick:()=>n(t),"aria-current":e===t?`page`:void 0,children:[(0,S.jsx)(i,{}),` `,l(r)]},t))}),(0,S.jsxs)(`div`,{className:`sidebar-foot`,children:[(0,S.jsxs)(`div`,{className:`lang-toggle`,children:[(0,S.jsx)(me,{"aria-hidden":!0}),(0,S.jsx)(Ae,{value:s,options:_e.map(e=>({value:e.code,label:e.name})),onChange:e=>c(e),label:l(`lang.label`),placement:`right`,style:{flex:1,minWidth:0,width:`100%`}})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle`,onClick:u,"aria-label":`${l(`theme.label`)}: ${l(Qt[r])}`,title:`${l(`theme.label`)}: ${l(Qt[r])}`,children:[(0,S.jsx)(d,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:l(Qt[r])})]}),(0,S.jsxs)(`button`,{type:`button`,className:`theme-toggle stop-toggle`,onClick:async()=>{if(confirm(l(`dash.stopConfirm`))){m(!0);try{await fetch(`${Jt}/api/stop`,{method:`POST`})}catch{}}},disabled:p,"aria-label":l(`dash.stop`),title:l(`dash.stop`),children:[(0,S.jsx)(I,{}),` `,(0,S.jsx)(`span`,{className:`mode`,children:l(p?`dash.stopping`:`dash.stop`)})]}),(0,S.jsxs)(`a`,{className:`sidebar-link`,href:`https://github.com/lidge-jun/opencodex`,target:`_blank`,rel:`noreferrer`,children:[(0,S.jsx)(F,{}),` `,l(`common.github`)]})]})]}),(0,S.jsx)(`main`,{className:`main`,children:(0,S.jsxs)(`div`,{className:`main-inner`,children:[e===`dashboard`&&(0,S.jsx)(He,{apiBase:Jt}),e===`providers`&&(0,S.jsx)(nt,{apiBase:Jt}),e===`models`&&(0,S.jsx)(ct,{apiBase:Jt}),e===`subagents`&&(0,S.jsx)(lt,{apiBase:Jt}),e===`logs`&&(0,S.jsx)(yt,{apiBase:Jt}),e===`debug`&&(0,S.jsx)(bt,{apiBase:Jt}),e===`usage`&&(0,S.jsx)(Dt,{apiBase:Jt}),e===`codex-auth`&&(0,S.jsx)(kt,{apiBase:Jt}),e===`api`&&(0,S.jsx)(Pt,{apiBase:Jt})]})})]})}v.createRoot(document.getElementById(`root`)).render((0,S.jsx)(_.StrictMode,{children:(0,S.jsx)(Se,{children:(0,S.jsx)(tn,{})})}));
|
package/gui/dist/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-DT4C-vKW.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-D7o1qwy-.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/package.json
CHANGED
package/src/server/responses.ts
CHANGED
|
@@ -133,25 +133,12 @@ export function collabSurface(parsed: OcxParsedRequest): "v1" | "v2" | null {
|
|
|
133
133
|
/**
|
|
134
134
|
* Multi-agent guidance for this turn, or null when nothing applies.
|
|
135
135
|
*
|
|
136
|
-
* codex-rs only emits its Proactive delegation developer message on the
|
|
137
|
-
* so when a v1-surface turn arrives at the synthetic top tier (codex
|
|
138
|
-
* ultra -> max on the wire, so max arrival means the user picked the top
|
|
139
|
-
* proxy supplies the same one-liner, wrapped in codex's own
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* toggle.
|
|
143
|
-
*
|
|
144
|
-
* Dynamic model injection: when the user has configured a specific injectionModel,
|
|
145
|
-
* the prompt names it so the agent knows which routed model to delegate to.
|
|
146
|
-
*
|
|
147
|
-
* Effort gate relaxation: when an injectionModel is set, the prompt fires at every
|
|
148
|
-
* effort level, not just max/ultra — the user opted into delegation.
|
|
149
|
-
*
|
|
150
|
-
* Reasoning-effort injection: when an injectionEffort is configured alongside the
|
|
151
|
-
* model, the prompt also tells the agent to pass `reasoning_effort` in spawn_agent
|
|
152
|
-
* calls (codex-rs validates spawn efforts by catalog membership; unsupported rungs
|
|
153
|
-
* are clamped on the wire). An effort WITHOUT a model changes nothing — the gate
|
|
154
|
-
* and the base prompt stay exactly as before.
|
|
136
|
+
* V1 surface: codex-rs only emits its Proactive delegation developer message on the
|
|
137
|
+
* v2 surface, so when a v1-surface turn arrives at the synthetic top tier (codex
|
|
138
|
+
* converts ultra -> max on the wire, so max arrival means the user picked the top
|
|
139
|
+
* rung) the proxy supplies the same one-liner, wrapped in codex's own
|
|
140
|
+
* <multi_agent_mode> tags. That is ALL v1 gets — no model designation, no roster
|
|
141
|
+
* (kept lean by request, devlog 260710): ultra-tier injection is sufficient there.
|
|
155
142
|
*
|
|
156
143
|
* V2 surface (flat spawn_agent — sol/terra under the default mode, EVERY model when
|
|
157
144
|
* `ocx v2 mode v2` forces the pins): codex-rs already emits its own Proactive text
|
|
@@ -169,16 +156,12 @@ export function collabSurface(parsed: OcxParsedRequest): "v1" | "v2" | null {
|
|
|
169
156
|
* regardless of the flag (spawn.rs), so the prompt tells the model to pass the
|
|
170
157
|
* arguments even though the schema does not list them.
|
|
171
158
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* `subagentModels` (the 5-list the catalog features for spawn_agent) are appended
|
|
179
|
-
* with the effort ladder each advertises in the injected catalog — the same list
|
|
180
|
-
* codex-rs validates spawn efforts against — so the agent can pick per-task models
|
|
181
|
-
* and honest effort rungs instead of flying blind behind the hidden schema.
|
|
159
|
+
* The v2 body is budgeted to <= 700 chars (V2_GUIDANCE_CHAR_BUDGET): rules first,
|
|
160
|
+
* then the preferred model, then the compact roster of configured `subagentModels`
|
|
161
|
+
* with the effort ladder each advertises in the injected catalog (the list codex-rs
|
|
162
|
+
* validates spawn efforts against). A user-configured `injectionPrompt` replaces the
|
|
163
|
+
* v2 body with {{model}}/{{effort}}/{{roster}} placeholder substitution (own length,
|
|
164
|
+
* user-owned); firing gates are unchanged.
|
|
182
165
|
*/
|
|
183
166
|
export async function multiAgentGuidanceText(parsed: OcxParsedRequest, injectionModel?: string, injectionEffort?: string, subagentModels?: string[], injectionPrompt?: string): Promise<string | null> {
|
|
184
167
|
const surface = collabSurface(parsed);
|
|
@@ -193,60 +176,33 @@ export async function multiAgentGuidanceText(parsed: OcxParsedRequest, injection
|
|
|
193
176
|
if (injectionPrompt) {
|
|
194
177
|
return `<multi_agent_mode>${applyInjectionPlaceholders(injectionPrompt, injectionModel, injectionEffort, roster)}</multi_agent_mode>`;
|
|
195
178
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"
|
|
199
|
-
|
|
200
|
-
"- With fork_turns \"none\", the child receives NO parent context: write a fully self-contained task message with paths, goals, and constraints.",
|
|
201
|
-
];
|
|
179
|
+
let text = "spawn_agent also accepts hidden \"model\" and \"reasoning_effort\" string arguments "
|
|
180
|
+
+ "(not in the schema, but parsed and applied) — never claim sub-agent models cannot be selected. "
|
|
181
|
+
+ "When setting either, set fork_turns to \"none\" (or e.g. \"3\"; full-history forks reject overrides) "
|
|
182
|
+
+ "and make the message self-contained.";
|
|
202
183
|
if (injectionModel) {
|
|
203
|
-
|
|
184
|
+
text += ` Preferred sub-agent: model "${injectionModel}"`
|
|
185
|
+
+ (injectionEffort ? `, reasoning_effort "${injectionEffort}"` : "")
|
|
186
|
+
+ " — use it unless the user names another.";
|
|
204
187
|
}
|
|
205
|
-
|
|
206
|
-
|
|
188
|
+
text += roster;
|
|
189
|
+
if (text.length > V2_GUIDANCE_CHAR_BUDGET) {
|
|
190
|
+
// Roster is the only unbounded part — drop it before breaking the budget.
|
|
191
|
+
text = text.slice(0, text.length - roster.length);
|
|
207
192
|
}
|
|
208
|
-
const exampleModel = injectionModel ?? "gpt-5.6-terra";
|
|
209
|
-
const example = "## Example spawn_agent call\n"
|
|
210
|
-
+ "```json\n"
|
|
211
|
-
+ JSON.stringify({
|
|
212
|
-
task_name: "example_task",
|
|
213
|
-
message: "Self-contained task description with paths, goals, and constraints.",
|
|
214
|
-
fork_turns: "none",
|
|
215
|
-
model: exampleModel,
|
|
216
|
-
...(injectionEffort ? { reasoning_effort: injectionEffort } : {}),
|
|
217
|
-
}, null, 2)
|
|
218
|
-
+ "\n```";
|
|
219
|
-
const text = `## Sub-agent model selection rules\n${rules.join("\n")}\n\n${example}${roster}`;
|
|
220
193
|
return `<multi_agent_mode>${text}</multi_agent_mode>`;
|
|
221
194
|
}
|
|
222
195
|
|
|
223
196
|
const effort = parsed.options.reasoning;
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
if (injectionPrompt) {
|
|
229
|
-
const roster = await subagentRosterText(subagentModels);
|
|
230
|
-
return `<multi_agent_mode>${applyInjectionPlaceholders(injectionPrompt, injectionModel, injectionEffort, roster)}</multi_agent_mode>`;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
let text = PROACTIVE_MULTI_AGENT_MODE_TEXT;
|
|
234
|
-
|
|
235
|
-
// Append the selected model when the user has configured a specific injection target.
|
|
236
|
-
if (injectionModel) {
|
|
237
|
-
text += `\n\nA preferred sub-agent model is configured: "${injectionModel}". `
|
|
238
|
-
+ `When delegating, call spawn_agent and set its model argument to exactly "${injectionModel}". `
|
|
239
|
-
+ "Use it for independent sub-tasks unless the user explicitly asks for another model.";
|
|
240
|
-
if (injectionEffort) {
|
|
241
|
-
text += ` A preferred sub-agent reasoning effort is also configured: "${injectionEffort}". `
|
|
242
|
-
+ `Set the reasoning_effort argument of spawn_agent to exactly "${injectionEffort}" for those sub-agents.`;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
text += await subagentRosterText(subagentModels);
|
|
247
|
-
return `<multi_agent_mode>${text}</multi_agent_mode>`;
|
|
197
|
+
// v1 keeps only the upstream-parity behavior: Proactive text at the top tier
|
|
198
|
+
// (ultra arrives as max on the wire). No designation/roster payload here.
|
|
199
|
+
if (effort !== "max" && effort !== "ultra") return null;
|
|
200
|
+
return `<multi_agent_mode>${PROACTIVE_MULTI_AGENT_MODE_TEXT}</multi_agent_mode>`;
|
|
248
201
|
}
|
|
249
202
|
|
|
203
|
+
/** Hard budget for the built-in v2 guidance body (user request: keep injection lean). */
|
|
204
|
+
export const V2_GUIDANCE_CHAR_BUDGET = 700;
|
|
205
|
+
|
|
250
206
|
/** {{model}}/{{effort}}/{{roster}} substitution for the user-configured injectionPrompt. */
|
|
251
207
|
function applyInjectionPlaceholders(prompt: string, model?: string, effort?: string, roster?: string): string {
|
|
252
208
|
return prompt
|
|
@@ -256,7 +212,7 @@ function applyInjectionPlaceholders(prompt: string, model?: string, effort?: str
|
|
|
256
212
|
}
|
|
257
213
|
|
|
258
214
|
/**
|
|
259
|
-
*
|
|
215
|
+
* Compact one-line roster of configured sub-agent models, or "" when no configured
|
|
260
216
|
* model resolves to a catalog entry. Efforts come from the injected catalog
|
|
261
217
|
* (catalogModelEfforts) so only rungs codex-rs will actually accept are advertised.
|
|
262
218
|
*/
|
|
@@ -265,13 +221,17 @@ async function subagentRosterText(subagentModels?: string[]): Promise<string> {
|
|
|
265
221
|
if (featured.length === 0) return "";
|
|
266
222
|
const { catalogModelEfforts } = await import("../codex/catalog");
|
|
267
223
|
const efforts = catalogModelEfforts(featured);
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
224
|
+
const resolved = featured.filter(id => efforts.has(id));
|
|
225
|
+
if (resolved.length === 0) return "";
|
|
226
|
+
const ladders = new Set(resolved.map(id => efforts.get(id)!.join("/")));
|
|
227
|
+
if (ladders.size === 1) {
|
|
228
|
+
// Shared ladder (the common case: the injected catalog advertises one rung set)
|
|
229
|
+
// -> state it once instead of per model, keeping the roster inside the budget.
|
|
230
|
+
const ids = resolved.map(id => `"${id}"`).join(", ");
|
|
231
|
+
return ` Available models (reasoning_effort ${[...ladders][0]}): ${ids}.`;
|
|
232
|
+
}
|
|
233
|
+
const entries = resolved.map(id => `"${id}" (${efforts.get(id)!.join("/")})`);
|
|
234
|
+
return ` Available models (valid reasoning_effort): ${entries.join(", ")}.`;
|
|
275
235
|
}
|
|
276
236
|
|
|
277
237
|
/**
|