@agentrouter-top/relay-dsh-plugin-codex 0.2.2-agentrouter.3 → 0.2.2-agentrouter.4

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.
@@ -0,0 +1,29 @@
1
+ # Optional Codex presentation policy, version 1
2
+
3
+ Relay owns Codex event projection, process/transcript presentation, kernel labels,
4
+ blank-session preset selection and the native model seat guard. Integration
5
+ plugins configure the browser Cordis service `relayCodexPresentation`; they do
6
+ not render Codex components. Types are exported from the `./presentation` subpath.
7
+
8
+ Wait for the service with `ctx.inject(['relayCodexPresentation'], ...)`, check
9
+ `version === 1`, then call `configure({ kernelMode: 'codex-only', hideFeedback:
10
+ true, hideStatistics: true })`. Register the returned disposer with the owner's
11
+ lifecycle. Only one configuration owner is accepted. Dispose before reconfiguring.
12
+ Without configuration Relay retains its upstream coexistence UI.
13
+
14
+ `kernelMode` defaults to `coexist`. `codex-only` enables the Codex label/settings,
15
+ selects Codex only for blank sessions through the public preset transaction, and
16
+ guards the existing DSH model picker. The integrating host remains responsible
17
+ for enforcing its execution policy. Existing sessions/presets are not deleted or
18
+ retargeted. Model catalogs and effort controls remain upstream implementations.
19
+
20
+ `hideFeedback` and `hideStatistics` default to false. They shadow only the native
21
+ feedback/statistics public leaf slots for the rendered Codex session. Other
22
+ kernels and other plugins' entries remain unchanged. Changed/unknown leaf
23
+ contracts are left visible. Unloading or disposing restores native entries.
24
+
25
+ The current DSH turn-tail component has no separate public switch for its copy,
26
+ branch, timing and timestamp actions. Version 1 does **not** hide that parent:
27
+ it also owns deliverable child slots. No private DOM/CSS manipulation, event
28
+ rewriting or DSH component fork is used. Files, errors, approvals and history are
29
+ unaffected. Account credentials and runtime launch options never enter this API.
package/lib/client.js CHANGED
@@ -36395,6 +36395,263 @@ window.__ModuleLoader__.load({
36395
36395
  });
36396
36396
  }
36397
36397
  //#endregion
36398
+ //#region presentation-api.mjs
36399
+ /** Public, optional browser presentation policy. No account/runtime configuration. */
36400
+ function createPresentationController(install) {
36401
+ let active;
36402
+ return {
36403
+ service: Object.freeze({
36404
+ version: 1,
36405
+ configure(options = {}) {
36406
+ if (active) throw new Error("Relay Codex presentation already has a configuration owner");
36407
+ const { kernelMode = "coexist", hideFeedback = false, hideStatistics = false } = options;
36408
+ if (!["coexist", "codex-only"].includes(kernelMode) || typeof hideFeedback !== "boolean" || typeof hideStatistics !== "boolean" || Object.keys(options).some((key) => ![
36409
+ "kernelMode",
36410
+ "hideFeedback",
36411
+ "hideStatistics"
36412
+ ].includes(key))) throw new TypeError("Unsupported Relay Codex presentation policy");
36413
+ const remove = install(Object.freeze({
36414
+ kernelMode,
36415
+ hideFeedback,
36416
+ hideStatistics
36417
+ }));
36418
+ const release = () => {
36419
+ if (active !== release) return;
36420
+ active = void 0;
36421
+ remove();
36422
+ };
36423
+ active = release;
36424
+ return release;
36425
+ }
36426
+ }),
36427
+ dispose: () => active?.()
36428
+ };
36429
+ }
36430
+ //#endregion
36431
+ //#region src/client/kernel-presentation.tsx
36432
+ const CODEX_PRESET = "relay-codex";
36433
+ const UNSUPPORTED_KERNEL = "当前会话不是 Codex 内核。此客户端暂不支持执行,请新建 Codex 会话;原会话和历史记录会保留。";
36434
+ const isCodex = (session) => sessionPreset(session) === CODEX_PRESET;
36435
+ function KernelSection() {
36436
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
36437
+ style: {
36438
+ maxWidth: 600,
36439
+ lineHeight: 1.7
36440
+ },
36441
+ "aria-label": "执行内核",
36442
+ children: [
36443
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: "执行内核" }),
36444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "当前产品仅支持 Codex App Server。" }),
36445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "模型、思考强度、工具和执行行为由 Codex 适配器提供。原生 DSH / DeepSeek 内核暂不提供执行入口。" }),
36446
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "已有预设、自建预设和会话没有被删除;非 Codex 历史仍可查看。通用插件的共存模式不限制宿主的其他内核。" })
36447
+ ]
36448
+ });
36449
+ }
36450
+ function KernelLabel({ sessionId, useSessions }) {
36451
+ const codex = useSessions((state) => isCodex(state.byId[sessionId]));
36452
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
36453
+ role: "status",
36454
+ "data-relay-codex-kernel": codex ? "codex" : "unsupported",
36455
+ style: {
36456
+ fontSize: 12,
36457
+ lineHeight: 1.4,
36458
+ maxWidth: "min(65vw, 440px)",
36459
+ whiteSpace: "normal"
36460
+ },
36461
+ title: codex ? "Codex App Server 执行内核" : UNSUPPORTED_KERNEL,
36462
+ children: codex ? "Codex" : "非 Codex 内核 · 当前不支持执行,请新建 Codex 会话"
36463
+ });
36464
+ }
36465
+ function KernelModelSeat({ Upstream, ...props }) {
36466
+ return props.useSessions((state) => isCodex(state.byId[props.sessionId])) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Upstream, { ...props }) : null;
36467
+ }
36468
+ function installModelSeatGuard(ctx) {
36469
+ const slot = "conversation.input.model";
36470
+ let source, release;
36471
+ const reconcile = () => {
36472
+ const next = ctx.slots.entries(slot).find((entry) => entry.locale === "model" && (entry.options.priority ?? 0) === 0);
36473
+ if (next === source) return;
36474
+ source = next;
36475
+ const previous = release;
36476
+ release = void 0;
36477
+ previous?.();
36478
+ if (!next?.inject || next.children || next.store) return;
36479
+ const original = next.inject;
36480
+ release = ctx.slots.register({
36481
+ name: slot,
36482
+ priority: -50,
36483
+ locale: "model",
36484
+ inject: (sessionId) => {
36485
+ const face = original(sessionId);
36486
+ return {
36487
+ ...face,
36488
+ Upstream: next.component,
36489
+ select: (selection) => isCodex(ctx.sessions.list.getSnapshot().byId[sessionId]) ? face.select(selection) : Promise.resolve(false)
36490
+ };
36491
+ }
36492
+ }, KernelModelSeat);
36493
+ };
36494
+ const stop = ctx.slots.subscribe(slot, reconcile);
36495
+ reconcile();
36496
+ return () => {
36497
+ stop();
36498
+ release?.();
36499
+ };
36500
+ }
36501
+ function CodexSeat({ useSessions, selectCodex }) {
36502
+ const session = useSessions((state) => state.current === void 0 ? void 0 : state.byId[state.current]);
36503
+ const id = session?.id;
36504
+ const preset = sessionPreset(session), blank = session?.blank;
36505
+ const [failure, setFailure] = (0, react.useState)();
36506
+ const [attempt, setAttempt] = (0, react.useState)(0);
36507
+ (0, react.useEffect)(() => {
36508
+ if (!id || !blank || preset === CODEX_PRESET) return;
36509
+ let stopped = false;
36510
+ setFailure(void 0);
36511
+ selectCodex(id).then((message) => {
36512
+ if (!stopped && message) setFailure({
36513
+ id,
36514
+ message
36515
+ });
36516
+ }).catch((error) => {
36517
+ if (!stopped) setFailure({
36518
+ id,
36519
+ message: error instanceof Error ? error.message : "无法选择 Codex 预设,请重试。"
36520
+ });
36521
+ });
36522
+ return () => {
36523
+ stopped = true;
36524
+ };
36525
+ }, [
36526
+ id,
36527
+ blank,
36528
+ preset,
36529
+ selectCodex,
36530
+ attempt
36531
+ ]);
36532
+ const error = preset !== CODEX_PRESET && failure?.id === id ? failure?.message : void 0;
36533
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
36534
+ style: {
36535
+ fontSize: 13,
36536
+ maxWidth: "60vw"
36537
+ },
36538
+ "aria-label": "Codex 执行内核",
36539
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
36540
+ role: "status",
36541
+ children: !id || preset === CODEX_PRESET ? "Codex" : error ? "Codex 未就绪" : "正在准备 Codex…"
36542
+ }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
36543
+ role: "alert",
36544
+ children: [
36545
+ error,
36546
+ " ",
36547
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
36548
+ type: "button",
36549
+ onClick: () => setAttempt((value) => value + 1),
36550
+ children: "重试"
36551
+ })
36552
+ ]
36553
+ })]
36554
+ });
36555
+ }
36556
+ /** Opt-in single-kernel product UI. Never retarget or delete existing history. */
36557
+ function installExclusiveKernelPresentation(ctx) {
36558
+ const selectCodex = async (id) => {
36559
+ const session = ctx.sessions.list.getSnapshot().byId[id];
36560
+ if (!session?.blank || isCodex(session)) return;
36561
+ const result = await ctx.remote.agentPresets.select(id, CODEX_PRESET);
36562
+ return result.ok ? void 0 : `${result.error.code}: ${result.error.message}`;
36563
+ };
36564
+ const releases = [];
36565
+ const dispose = () => {
36566
+ while (releases.length) releases.pop()();
36567
+ };
36568
+ try {
36569
+ releases.push(ctx.slots.inject("conversation.input.model", () => installModelSeatGuard(ctx)));
36570
+ releases.push(ctx.slots.inject("conversation.hero.agentPreset", () => ctx.slots.register({
36571
+ name: "conversation.hero.agentPreset",
36572
+ priority: -50,
36573
+ inject: () => ({ selectCodex })
36574
+ }, CodexSeat)));
36575
+ releases.push(ctx.slots.inject("settings.section", () => ctx.slots.register({
36576
+ name: "settings.section",
36577
+ id: "relay-codex-kernels",
36578
+ order: 20,
36579
+ label: () => "执行内核"
36580
+ }, KernelSection)));
36581
+ releases.push(ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
36582
+ name: "conversation.session.header.actions",
36583
+ id: "relay-codex-kernel",
36584
+ order: -10
36585
+ }, KernelLabel)));
36586
+ return dispose;
36587
+ } catch (error) {
36588
+ dispose();
36589
+ throw error;
36590
+ }
36591
+ }
36592
+ //#endregion
36593
+ //#region src/client/presentation.tsx
36594
+ function NativeNonCodexLeaf({ Upstream, ...props }) {
36595
+ return props.useSessions((state) => sessionPreset(state.byId[props.sessionId]) === "relay-codex") ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Upstream, { ...props });
36596
+ }
36597
+ /** Shadow only a known public leaf. Parent ownership and unrelated entries stay intact. */
36598
+ function hideNativeLeaf(ctx, name, id) {
36599
+ return ctx.slots.inject(name, () => {
36600
+ let source, release;
36601
+ const reconcile = () => {
36602
+ const next = ctx.slots.entries(name).find((entry) => entry.options.id === id && (entry.options.priority ?? 0) === 0);
36603
+ if (next === source) return;
36604
+ source = next;
36605
+ const previous = release;
36606
+ release = void 0;
36607
+ previous?.();
36608
+ if (!next || next.children || next.store) return;
36609
+ if (name === "conversation.composer.dock" && next.inject) return;
36610
+ if (next.locale !== void 0 && next.locale !== "chat" && next.locale !== "conversation" && next.locale !== "feedback") return;
36611
+ const originalInject = next.inject;
36612
+ release = ctx.slots.register({
36613
+ name,
36614
+ id,
36615
+ priority: -50,
36616
+ order: next.options.order,
36617
+ locale: next.locale,
36618
+ inject: (sessionId) => ({
36619
+ ...originalInject?.(sessionId),
36620
+ Upstream: next.component
36621
+ })
36622
+ }, NativeNonCodexLeaf);
36623
+ };
36624
+ const stop = ctx.slots.subscribe(name, reconcile);
36625
+ reconcile();
36626
+ return () => {
36627
+ stop();
36628
+ release?.();
36629
+ };
36630
+ });
36631
+ }
36632
+ function installPresentationPolicy(ctx, options) {
36633
+ const releases = [];
36634
+ const dispose = () => {
36635
+ while (releases.length) releases.pop()();
36636
+ };
36637
+ try {
36638
+ if (options.kernelMode === "codex-only") releases.push(installExclusiveKernelPresentation(ctx));
36639
+ if (options.hideStatistics) releases.push(hideNativeLeaf(ctx, "conversation.composer.dock", "stats"));
36640
+ if (options.hideFeedback) releases.push(hideNativeLeaf(ctx, "conversation.chat.assistant-actions", "feedback"));
36641
+ return dispose;
36642
+ } catch (error) {
36643
+ dispose();
36644
+ throw error;
36645
+ }
36646
+ }
36647
+ function installPresentationService(ctx) {
36648
+ ctx.inject(["remote", "remote.agentPresets"], (scope) => {
36649
+ const controller = createPresentationController((options) => installPresentationPolicy(scope, options));
36650
+ scope.effect(() => controller.dispose, "relay-codex: optional presentation policy");
36651
+ scope.provide("relayCodexPresentation", controller.service);
36652
+ });
36653
+ }
36654
+ //#endregion
36398
36655
  //#region src/client/codex-activity.ts
36399
36656
  const codexActivityDefinition = {
36400
36657
  kind: "relay-codex-activity",
@@ -37161,6 +37418,7 @@ window.__ModuleLoader__.load({
37161
37418
  "connection"
37162
37419
  ];
37163
37420
  function apply(ctx) {
37421
+ installPresentationService(ctx);
37164
37422
  const advancedDebug = applyAdvancedDebug(ctx);
37165
37423
  applyWorkspaceImport(ctx);
37166
37424
  applySessionOpenSync(ctx);