Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions apps/presentation/dashboard/src/data/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,31 @@ export async function interruptChatTurn(sessionId: string, turnId: string) {
);
}

export type LoopXModeSettings = { agent_id: string; token_budget: number; execution_config: string };
export type LoopXModeSnapshot = {
ok: true; session_id: string; enabled: boolean; active_turn_id: string | null; conversation_busy: boolean;
settings: Partial<LoopXModeSettings>;
native: { status: string; tokenBudget?: number; tokensUsed?: number };
registered_agents: string[]; paused: boolean; recovery_required: boolean;
members: Array<{id: string; agent_id: string; todo_id: string}>;
deliveries: Array<{operation_id: string; agent_id: string; todo_id: string; status: string}>;
ingress: Array<{client_ingress_id: string; mode: string; status: string}>;
turn_id?: string;
};
export function fetchLoopXMode(sessionId: string) {
return requestJson<LoopXModeSnapshot>(`/api/chat/sessions/${sessionId}/loopx`);
}
export function updateLoopXMode(sessionId: string, operation: string, settings?: LoopXModeSettings, operationId = crypto.randomUUID()) {
return requestJson<LoopXModeSnapshot>(`/api/chat/sessions/${sessionId}/loopx`, {
method: "POST", body: JSON.stringify({operation, operation_id: operationId, ...(settings ? {settings} : {})}),
});
}
export function sendLoopXMessage(sessionId: string, message: string, deliveryMode: "queue" | "inbox" | "steer") {
return requestJson<{ok: true; status: string; delivery_mode: string}>(`/api/chat/sessions/${sessionId}/loopx`, {
method: "POST", body: JSON.stringify({operation: "message", operation_id: crypto.randomUUID(), message, delivery_mode: deliveryMode}),
});
}

export async function sendChatTurnStreaming(
sessionId: string,
message: string,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import {useEffect, useState} from "react";
import {Pause, Play, Settings2} from "lucide-react";
import {fetchLoopXMode, updateLoopXMode, type LoopXModeSnapshot, type LoopXModeSettings} from "../../data/chat";
import {useWorkspaceI18n} from "./i18n";
import "./goal-loopx-mode.css";

export function GoalLoopXMode({sessionId, onPrepare, onExecute, onChange}: {
sessionId?: string;
onPrepare: () => Promise<string>;
onExecute: (operation: "start" | "resume", settings?: LoopXModeSettings) => void;
onChange: (snapshot: LoopXModeSnapshot | null) => void;
}) {
const {locale} = useWorkspaceI18n();
const zh = locale === "zh-CN";
const [snapshot, setSnapshot] = useState<LoopXModeSnapshot | null>(null);
const [editing, setEditing] = useState(false);
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
const [settings, setSettings] = useState<LoopXModeSettings>({agent_id: "", token_budget: 0, execution_config: ".loopx/config/delegations.json"});
useEffect(() => {
let alive = true;
let refreshing = false;
setSnapshot(null); onChange(null); setError(""); setEditing(false);
if (!sessionId || sessionId === "new-session-pending") return;
async function refresh() {
if (refreshing) return;
refreshing = true;
try {
const result = await fetchLoopXMode(sessionId!);
if (alive) {setSnapshot(result); onChange(result);}
} catch (failure) {if (alive) setError(failure instanceof Error ? failure.message : String(failure));}

Check warning on line 31 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The catch parameter `failure` should be named `error_`.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnxx&open=AaC4uxAMynaeS38RBnxx&pullRequest=4700
finally {refreshing = false;}
}
void refresh();
const interval = window.setInterval(() => {if (!document.hidden) void refresh();}, 2500);
return () => {alive = false; window.clearInterval(interval);};
}, [sessionId]); // onChange is the owning component's stable state setter.
const active = Boolean(snapshot?.enabled && snapshot.active_turn_id);
const native = snapshot?.native.status ?? "absent";
const resume = !["absent", "complete"].includes(native);
const configured = Boolean(snapshot?.settings.agent_id && snapshot.settings.token_budget);
const editSettings = (current: LoopXModeSnapshot) => {
setSettings({agent_id: current.settings.agent_id ?? "", token_budget: current.settings.token_budget ?? 0,
execution_config: current.settings.execution_config ?? ".loopx/config/delegations.json"});
setEditing(true);
};
const status = !snapshot?.enabled ? (zh ? "普通对话" : "Conversation")

Check warning on line 47 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnxy&open=AaC4uxAMynaeS38RBnxy&pullRequest=4700
: snapshot.recovery_required ? (zh ? "LoopX · 需要恢复连接" : "LoopX · Reconnect required")
: native === "blocked" ? (zh ? "LoopX · 需要处理阻塞" : "LoopX · Blocked")
: active ? (zh ? "LoopX · 正在推进" : "LoopX · Working")

Check warning on line 50 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx4&open=AaC4uxAMynaeS38RBnx4&pullRequest=4700
: native === "complete" ? (zh ? "LoopX · 本轮已结束" : "LoopX · Run finished")

Check warning on line 51 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx6&open=AaC4uxAMynaeS38RBnx6&pullRequest=4700
: ["budgetLimited", "usageLimited"].includes(native) ? (zh ? "LoopX · 已到额度限制" : "LoopX · Usage limit")

Check warning on line 52 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx8&open=AaC4uxAMynaeS38RBnx8&pullRequest=4700
: (zh ? "LoopX · 已暂停" : "LoopX · Paused");

Check warning on line 53 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx1&open=AaC4uxAMynaeS38RBnx1&pullRequest=4700

Check warning on line 53 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnxz&open=AaC4uxAMynaeS38RBnxz&pullRequest=4700

Check warning on line 53 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx7&open=AaC4uxAMynaeS38RBnx7&pullRequest=4700

Check warning on line 53 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx5&open=AaC4uxAMynaeS38RBnx5&pullRequest=4700
const openSettings = () => {
if (snapshot && !editing) editSettings(snapshot);
else setEditing(false);
};
async function prepareSettings() {
setBusy(true); setError("");
try {
const preparedSessionId = await onPrepare();
const result = await fetchLoopXMode(preparedSessionId);
setSnapshot(result); onChange(result); editSettings(result);
} catch (failure) {setError(failure instanceof Error ? failure.message : String(failure));}

Check warning on line 64 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The catch parameter `failure` should be named `error_`.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnx-&open=AaC4uxAMynaeS38RBnx-&pullRequest=4700
finally {setBusy(false);}
}
async function mutate(operation: string) {
if (!sessionId) return;
setBusy(true); setError("");
try {const result = await updateLoopXMode(sessionId, operation, operation === "configure" ? settings : undefined);
setSnapshot(result); onChange(result); setEditing(false);
} catch (failure) {setError(failure instanceof Error ? failure.message : String(failure));}
finally {setBusy(false);}
}
return <section className="goal-loopx-mode" aria-label={zh ? "LoopX 运行模式" : "LoopX execution mode"}>
<div className="goal-loopx-mode-bar"><div><strong>{status}</strong><p>{active
? (zh ? "协调员持续工作;成员保持独立执行与验收。" : "The coordinator continues; members execute and qualify independently.")

Check warning on line 77 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyA&open=AaC4uxAMynaeS38RBnyA&pullRequest=4700
: snapshot?.enabled && native === "complete" ? (zh ? "本次执行已结束;整个 Goal 仍按原标准验收。" : "This run ended; canonical Goal acceptance remains separate.")

Check warning on line 78 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyC&open=AaC4uxAMynaeS38RBnyC&pullRequest=4700
: snapshot?.enabled ? (zh ? "暂停不会停止已派发成员;整个 Goal 仍按原标准验收。" : "Pausing retains delegated work; Goal acceptance remains separate.")

Check warning on line 79 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyE&open=AaC4uxAMynaeS38RBnyE&pullRequest=4700
: (zh ? "开启后,当前协调员持续推进本 Goal。" : "Enable continued work on this Goal in this conversation.")}</p></div>

Check warning on line 80 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyB&open=AaC4uxAMynaeS38RBnyB&pullRequest=4700

Check warning on line 80 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyF&open=AaC4uxAMynaeS38RBnyF&pullRequest=4700
<div className="goal-loopx-mode-actions"><button type="button" disabled={busy || snapshot?.conversation_busy || !snapshot} onClick={openSettings} aria-expanded={editing}><Settings2 size={14}/>{zh ? "运行设置" : "Settings"}</button>
<button type="button" disabled={busy || Boolean(snapshot?.conversation_busy && !active)} onClick={async () => {
if (!snapshot) {
await prepareSettings();
return;
}
if (active) void mutate("pause");
else if (!configured || Number(snapshot?.settings.token_budget ?? 0) <= Number(snapshot?.native.tokensUsed ?? 0)) openSettings();
else onExecute(resume ? "resume" : "start");
}}>{active ? <Pause size={14}/> : <Play size={14}/>}{active ? (zh ? "暂停" : "Pause") : !snapshot?.enabled ? (zh ? "开启 LoopX 模式" : "Enable LoopX") : native === "complete" ? (zh ? "开启新一轮" : "Start new run") : (zh ? "恢复推进" : "Continue")}</button>

Check warning on line 90 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyG&open=AaC4uxAMynaeS38RBnyG&pullRequest=4700

Check warning on line 90 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyH&open=AaC4uxAMynaeS38RBnyH&pullRequest=4700

Check warning on line 90 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyJ&open=AaC4uxAMynaeS38RBnyJ&pullRequest=4700
{snapshot?.enabled && !active ? <button type="button" disabled={busy} onClick={() => void mutate("exit")}>{zh ? "退出模式" : "Exit mode"}</button> : null}
</div></div>
{editing ? <div className="goal-loopx-mode-settings"><label>{zh ? "已注册的协调身份" : "Registered coordinator"}<select value={settings.agent_id} onChange={event => setSettings({...settings, agent_id: event.target.value})}><option value="">{zh ? "选择已授权身份" : "Select authorized identity"}</option>{snapshot?.registered_agents.map(id => <option key={id} value={id}>{id}</option>)}</select></label>
<label>{zh ? "协调员总 token 额度" : "Coordinator total token allowance"}<input type="number" min={1} max={2147483647} value={settings.token_budget || ""} onChange={event => setSettings({...settings, token_budget: Number(event.target.value)})}/></label>
<label>{zh ? "成员执行绑定文件(项目内)" : "Member execution bindings (project relative)"}<input value={settings.execution_config} onChange={event => setSettings({...settings, execution_config: event.target.value})}/></label>
<p>{zh ? "复用现有协作执行配置,文件须位于 .loopx/config/。额度包含协调员历史用量;成员沿用各自授权,不随开启扩大。" : "Reuse an existing delegation configuration under .loopx/config/. The allowance includes coordinator history; member grants remain separate."}</p>
<button type="button" disabled={busy || !settings.agent_id || settings.token_budget < 1} onClick={() => void mutate("configure")}>{zh ? "保存设置" : "Save settings"}</button></div> : null}
{snapshot?.enabled && snapshot.native.tokensUsed !== undefined ? <p className="goal-loopx-mode-usage">{zh ? "协调员累计用量" : "Coordinator usage"} {snapshot.native.tokensUsed.toLocaleString()} / {snapshot.native.tokenBudget?.toLocaleString() ?? "—"} tokens</p> : null}
{snapshot?.enabled && snapshot.ingress.some(row => row.status !== "delivered") ? <p role="status">{zh ? "待处理消息:" : "Pending messages: "}{snapshot.ingress.filter(row => row.status !== "delivered").map(row => `${row.mode === "loopx_queue" ? "queue" : "inbox"} · ${row.status}`).join(" / ")}</p> : null}
{snapshot?.enabled && snapshot.deliveries.length ? <div className="goal-loopx-mode-members" aria-label={zh ? "最近一次成员回读" : "Last member observations"}><span>{zh ? "成员最近回读" : "Last observations"}</span>{snapshot.deliveries.map(row => <span key={row.operation_id}>{row.agent_id} · {row.status === "accepted" ? (zh ? "已通过验收" : "Accepted") : row.status === "rejected" ? (zh ? "未通过验收" : "Rejected") : row.status === "unavailable" ? (zh ? "需要重新核验" : "Recheck required") : (zh ? "执行中" : "Working")}</span>)}</div> : null}

Check warning on line 100 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyT&open=AaC4uxAMynaeS38RBnyT&pullRequest=4700

Check warning on line 100 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyO&open=AaC4uxAMynaeS38RBnyO&pullRequest=4700

Check warning on line 100 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyR&open=AaC4uxAMynaeS38RBnyR&pullRequest=4700

Check warning on line 100 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyS&open=AaC4uxAMynaeS38RBnyS&pullRequest=4700

Check failure on line 100 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyN&open=AaC4uxAMynaeS38RBnyN&pullRequest=4700

Check warning on line 100 in apps/presentation/dashboard/src/features/personal-workspace/goal-loopx-mode.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxAMynaeS38RBnyQ&open=AaC4uxAMynaeS38RBnyQ&pullRequest=4700
{error ? <p className="personal-composer-error" role="alert">{error}</p> : null}
</section>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,9 @@ export type PersonalWorkspaceCallbacks = {
goalId: string | null,
attachments?: WorkspaceImageAttachment[],
) => void | WorkspaceActionPreviewRequest | Promise<void | WorkspaceActionPreviewRequest>;
onPrepareLoopX?: (agentId: string, goalId: string) => Promise<string>;
onStartLoopX?: (operation: "start" | "resume", agentId: string, goalId: string,
settings?: import("../../data/chat").LoopXModeSettings) => void;
onSelectAgent?: (agentId: string) => void;
onSelectChannel?: (channel: WorkspaceChannel) => void;
onSelectGoal?: (goalId: string | null) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
type ManagerChannelBinding,
type ManagerRuntimeSessionReadback,
type TypedActionProposal,
} from "../../data/chat";

Check warning on line 29 in apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'../../data/chat' imported multiple times.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxCZynaeS38RBnyU&open=AaC4uxCZynaeS38RBnyU&pullRequest=4700

import { ChannelHeader } from "./channel-header";
import { GoalLoopXMode } from "./goal-loopx-mode";
import { sendLoopXMessage, type LoopXModeSnapshot } from "../../data/chat";
import { ChannelTimeline } from "./channel-timeline";
import { ContextDrawer } from "./context-drawer";
import { GoalSidebar } from "./goal-sidebar";
Expand Down Expand Up @@ -772,6 +774,7 @@
}

export function PersonalWorkspacePage({
conversationSessionId,
agents = [{ agentId: "codex", available: true, capability: "代码与项目执行", label: "Codex" }],
callbacks = {},
goalArchiveLoadState = { error: null, phase: "ready" },
Expand All @@ -783,6 +786,7 @@
selectedGoalId: controlledGoalId,
statusSourceControl,
}: {
conversationSessionId?: string;
agents?: WorkspaceAgentOption[];
callbacks?: PersonalWorkspaceCallbacks;
goalArchiveLoadState?: WorkspaceGoalArchiveLoadState;
Expand Down Expand Up @@ -818,6 +822,9 @@
}
});
const [sending, setSending] = useState(false);
const [loopxMode, setLoopxMode] = useState<LoopXModeSnapshot | null>(null);
const [loopxDelivery, setLoopxDelivery] = useState<"queue" | "inbox" | "steer">("queue");
const [loopxMessageReceipt, setLoopxMessageReceipt] = useState("");
const [imageAttachments, setImageAttachments] = useState<WorkspaceImageAttachment[]>([]);
const [imageAttachmentError, setImageAttachmentError] = useState<string | null>(null);
const [actionFeedback, setActionFeedback] = useState<string | null>(null);
Expand Down Expand Up @@ -1638,6 +1645,20 @@
const pendingImages = messageOverride ? [] : imageAttachments;
const message = (messageOverride ?? composer).trim() || (pendingImages.length ? t("composer.imageAnalysisPrompt") : "");
if (!message || sending) return;
if (loopxMode?.session_id === conversationSessionId && loopxMode?.enabled && loopxMode.active_turn_id && conversationSessionId) {
if (pendingImages.length) {
setImageAttachmentError(locale === "zh-CN" ? "运行中的消息投递暂不支持图片,请暂停后发送。" : "Pause execution before sending images.");
return;
}
setSending(true);
try {
const receipt = await sendLoopXMessage(conversationSessionId, message, loopxDelivery);
if (!messageOverride) setComposer("");
setLoopxMessageReceipt(locale === "zh-CN" ? `${loopxDelivery === "queue" ? "已排队,等待后续回合" : loopxDelivery === "inbox" ? "已进入收件箱" : "已提交纠偏"} · ${receipt.status}` : `${loopxDelivery}: ${receipt.status}`);

Check warning on line 1657 in apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxCZynaeS38RBnyW&open=AaC4uxCZynaeS38RBnyW&pullRequest=4700

Check warning on line 1657 in apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaC4uxCZynaeS38RBnyX&open=AaC4uxCZynaeS38RBnyX&pullRequest=4700
} catch (error) {setImageAttachmentError(error instanceof Error ? error.message : String(error));}
finally {setSending(false);}
return;
}
if (!messageOverride) {
setComposer("");
setImageAttachments([]);
Expand Down Expand Up @@ -1999,6 +2020,12 @@
)}
</div>
<div className="personal-composer-wrap">
{selectedGoalId && selectedGoalTab === "chat" && !readOnly && selectedAgentId === "codex" && callbacks.onStartLoopX ? <GoalLoopXMode
onPrepare={() => callbacks.onPrepareLoopX!(selectedAgentId, selectedGoalId)}
key={`${selectedGoalId}:${selectedAgentId}`} sessionId={conversationSessionId} onChange={setLoopxMode}
onExecute={(operation, settings) => callbacks.onStartLoopX?.(operation, selectedAgentId, selectedGoalId, settings)}
/> : null}
{loopxMode?.session_id === conversationSessionId && loopxMode?.enabled && loopxMode.active_turn_id ? <label className="goal-loopx-message-mode">{locale === "zh-CN" ? "消息处理" : "Message delivery"}<select aria-label={locale === "zh-CN" ? "消息处理方式" : "Message delivery mode"} value={loopxDelivery} onChange={event => setLoopxDelivery(event.target.value as typeof loopxDelivery)}><option value="queue">{locale === "zh-CN" ? "下一轮处理" : "Next turn"}</option><option value="inbox">{locale === "zh-CN" ? "放入收件箱" : "Inbox"}</option><option value="steer">{locale === "zh-CN" ? "立即纠偏" : "Steer now"}</option></select><span role="status">{loopxMessageReceipt}</span></label> : null}
{readOnly ? (
<div className="personal-read-only-notice"><strong>{t("source.readOnlyNoticeTitle")}</strong><span>{t("source.readOnlyNoticeDescription")}</span></div>
) : <>
Expand Down
Loading
Loading