mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-26 20:45:37 +02:00
refactor(views): transcript header — two-tier by necessity (ⓘ popover), trigger source, full status machine
Following PR #5747's design lesson: split header metadata by whether a viewer needs it BEFORE reading. The flat facts line put diagnostics (provider, runtime hostname, mode, workdir, timestamps) on the always-visible surface with equal weight. - Identity row (tier 1): status · agent · trigger source · attribution. Trigger source ("Initial run" / "From a comment" / "Retry" / ...) answers "why does this run exist" — restored from #5747, was dropped. - Status badge now covers the full state machine (queued / dispatched / cancelled / waiting), not just running/completed/failed. - ⓘ Run-details popover (tier 2): runtime, provider, mode, workdir (copyable), created/started/completed — off the default surface. - Toolbar left carries duration + event/tool counts (a read-before-you- read summary), so the control row balances instead of stranding the controls at the right with empty left space. Persisted preferences (sort, filter selection, preserve-filters, expand density) are untouched — same controls, same behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -306,9 +306,9 @@ describe("AgentTranscriptDialog", () => {
|
||||
|
||||
renderDialog([], { task: liveTask, isLive: true });
|
||||
|
||||
// The runtime's display name lives in the facts line's hover title; the
|
||||
// visible text is the runtime mode.
|
||||
await screen.findByTitle("hermes runtime");
|
||||
// Runtime detail now lives in the ⓘ popover; its trigger appearing proves
|
||||
// the runtime loaded. The non-antigravity live state still waits.
|
||||
await screen.findByRole("button", { name: "Run details" });
|
||||
expect(screen.getByText("Waiting for events...")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -14,18 +14,16 @@ import {
|
||||
Clock,
|
||||
Copy,
|
||||
Check,
|
||||
Monitor,
|
||||
Cloud,
|
||||
Cpu,
|
||||
Filter,
|
||||
Folder,
|
||||
ArrowDownNarrowWide,
|
||||
ArrowUpNarrowWide,
|
||||
ListCollapse,
|
||||
Info,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@multica/ui/lib/utils";
|
||||
import { copyText } from "@multica/ui/lib/clipboard";
|
||||
import { Dialog, DialogContent, DialogTitle } from "@multica/ui/components/ui/dialog";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@multica/ui/components/ui/popover";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@multica/ui/components/ui/collapsible";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -133,6 +131,63 @@ function formatElapsedMs(ms: number): string {
|
||||
return `${minutes}m ${secs}s`;
|
||||
}
|
||||
|
||||
function formatRunTime(iso: string): string {
|
||||
return new Date(iso).toLocaleString(undefined, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Run detail row (ⓘ popover) ─────────────────────────────────────────────
|
||||
// One labeled fact in the diagnostic popover. When `onCopy` is given the whole
|
||||
// row is a copy button (used for the workdir path).
|
||||
function RunDetailRow({
|
||||
label,
|
||||
value,
|
||||
mono,
|
||||
onCopy,
|
||||
copied,
|
||||
copyTitle,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
mono?: boolean;
|
||||
onCopy?: () => void;
|
||||
copied?: boolean;
|
||||
copyTitle?: string;
|
||||
}) {
|
||||
const valueClass = cn("min-w-0 select-text break-all text-foreground/80", mono && "font-mono");
|
||||
if (onCopy) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCopy}
|
||||
title={copyTitle}
|
||||
className="group -mx-1 grid w-[calc(100%+0.5rem)] grid-cols-[4.5rem_minmax(0,1fr)] items-start gap-3 rounded px-1 py-0.5 text-left transition-colors hover:bg-accent/60"
|
||||
>
|
||||
<span className="text-muted-foreground">{label}</span>
|
||||
<span className="flex min-w-0 items-start gap-1.5">
|
||||
<span className={cn(valueClass, "flex-1")}>{value}</span>
|
||||
{copied ? (
|
||||
<Check className="mt-0.5 h-3 w-3 shrink-0 text-success" />
|
||||
) : (
|
||||
<Copy className="mt-0.5 h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="grid grid-cols-[4.5rem_minmax(0,1fr)] items-start gap-3">
|
||||
<span className="text-muted-foreground">{label}</span>
|
||||
<span className={valueClass}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Main dialog ────────────────────────────────────────────────────────────
|
||||
|
||||
// Virtuoso mounts rows as direct children of its List element; carry the
|
||||
@@ -388,27 +443,95 @@ export function AgentTranscriptDialog({
|
||||
? t(($) => $.transcript.copy_filtered)
|
||||
: t(($) => $.transcript.copy_all);
|
||||
|
||||
// Status display
|
||||
const statusBadge = isLive ? (
|
||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-info/15 px-2 py-0.5 text-xs font-medium text-info">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
{t(($) => $.transcript.status_running)}
|
||||
</span>
|
||||
) : task.status === "completed" ? (
|
||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-success/15 px-2 py-0.5 text-xs font-medium text-success">
|
||||
<CheckCircle2 className="h-3 w-3" />
|
||||
{t(($) => $.transcript.status_completed)}
|
||||
</span>
|
||||
) : task.status === "failed" ? (
|
||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-destructive/15 px-2 py-0.5 text-xs font-medium text-destructive">
|
||||
<XCircle className="h-3 w-3" />
|
||||
{t(($) => $.transcript.status_failed)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground capitalize">
|
||||
{task.status}
|
||||
</span>
|
||||
);
|
||||
// Status badge — full state machine, so queued/dispatched/cancelled render as
|
||||
// proper labels instead of raw enum text.
|
||||
const effectiveStatus = isLive ? "running" : task.status;
|
||||
const statusBadge = (() => {
|
||||
const base = "inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium";
|
||||
switch (effectiveStatus) {
|
||||
case "running":
|
||||
return (
|
||||
<span className={cn(base, "bg-info/15 text-info")}>
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
{t(($) => $.transcript.status_running)}
|
||||
</span>
|
||||
);
|
||||
case "completed":
|
||||
return (
|
||||
<span className={cn(base, "bg-success/15 text-success")}>
|
||||
<CheckCircle2 className="h-3 w-3" />
|
||||
{t(($) => $.transcript.status_completed)}
|
||||
</span>
|
||||
);
|
||||
case "failed":
|
||||
return (
|
||||
<span className={cn(base, "bg-destructive/15 text-destructive")}>
|
||||
<XCircle className="h-3 w-3" />
|
||||
{t(($) => $.transcript.status_failed)}
|
||||
</span>
|
||||
);
|
||||
case "cancelled":
|
||||
return (
|
||||
<span className={cn(base, "bg-muted text-muted-foreground")}>
|
||||
<XCircle className="h-3 w-3" />
|
||||
{t(($) => $.transcript.status_cancelled)}
|
||||
</span>
|
||||
);
|
||||
case "queued":
|
||||
return (
|
||||
<span className={cn(base, "bg-muted text-muted-foreground")}>
|
||||
{t(($) => $.transcript.status_queued)}
|
||||
</span>
|
||||
);
|
||||
case "dispatched":
|
||||
return (
|
||||
<span className={cn(base, "bg-info/15 text-info")}>
|
||||
{t(($) => $.transcript.status_dispatched)}
|
||||
</span>
|
||||
);
|
||||
case "waiting_local_directory":
|
||||
return (
|
||||
<span className={cn(base, "bg-muted text-muted-foreground")}>
|
||||
{t(($) => $.transcript.status_waiting)}
|
||||
</span>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<span className={cn(base, "bg-muted text-muted-foreground capitalize")}>
|
||||
{task.status}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
// Trigger source: one word answering "why does this run exist" — more useful
|
||||
// up front than the runtime/provider diagnostics, which move to the ⓘ popover.
|
||||
const triggerLabel = task.parent_task_id
|
||||
? t(($) => $.transcript.trigger_retry)
|
||||
: task.kind === "comment" || task.trigger_comment_id
|
||||
? t(($) => $.transcript.trigger_comment)
|
||||
: task.kind === "autopilot" || task.autopilot_run_id
|
||||
? t(($) => $.transcript.trigger_autopilot)
|
||||
: task.kind === "chat" || task.chat_session_id
|
||||
? t(($) => $.transcript.trigger_chat)
|
||||
: task.kind === "quick_create"
|
||||
? t(($) => $.transcript.trigger_quick_create)
|
||||
: task.kind === "direct" || task.handoff_note
|
||||
? t(($) => $.transcript.trigger_direct)
|
||||
: t(($) => $.transcript.trigger_initial);
|
||||
|
||||
// Diagnostic detail for the ⓘ popover: everything a reader needs only when
|
||||
// debugging this specific run, kept off the always-visible surface.
|
||||
const providerLabel = runtimeInfo?.provider ? formatProvider(runtimeInfo.provider) : null;
|
||||
const createdLabel = task.created_at ? formatRunTime(task.created_at) : null;
|
||||
const startedLabel = task.started_at ? formatRunTime(task.started_at) : null;
|
||||
const completedLabel = task.completed_at ? formatRunTime(task.completed_at) : null;
|
||||
const hasRunDetails =
|
||||
!!runtimeInfo ||
|
||||
!!task.relative_work_dir ||
|
||||
!!createdLabel ||
|
||||
!!startedLabel ||
|
||||
!!completedLabel;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
@@ -418,124 +541,118 @@ export function AgentTranscriptDialog({
|
||||
>
|
||||
<DialogTitle className="sr-only">{t(($) => $.transcript.dialog_title)}</DialogTitle>
|
||||
|
||||
{/* ── Header ─────────────────────────────────────────────── */}
|
||||
<div className="border-b px-4 py-3 shrink-0 space-y-1.5">
|
||||
{/* Identity row: outcome first, then who ran and on whose behalf.
|
||||
Status is the one fact every viewer opens this dialog for, so it
|
||||
anchors the left edge. Entities render through their existing
|
||||
identity components (avatar + hover card), never ad-hoc chips. */}
|
||||
{/* ── Header: identity only ──────────────────────────────────
|
||||
Tier 1 — everything a viewer needs BEFORE reading: outcome
|
||||
(status anchors the left), who ran it, why it exists (trigger),
|
||||
and who's accountable. All diagnostics move to the ⓘ popover. */}
|
||||
<div className="border-b px-4 py-3 shrink-0">
|
||||
<div className="flex min-w-0 items-center gap-2.5">
|
||||
{statusBadge}
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
{task.agent_id ? (
|
||||
<ActorAvatar actorType="agent" actorId={task.agent_id} size="sm" enableHoverCard />
|
||||
) : (
|
||||
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-info/10 text-info">
|
||||
<Bot className="h-3 w-3" />
|
||||
</div>
|
||||
)}
|
||||
<span className="truncate font-medium text-sm">
|
||||
{agentName || agentInfo?.name || ""}
|
||||
</span>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-x-3 overflow-hidden">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
{task.agent_id ? (
|
||||
<ActorAvatar actorType="agent" actorId={task.agent_id} size="sm" enableHoverCard />
|
||||
) : (
|
||||
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-info/10 text-info">
|
||||
<Bot className="h-3 w-3" />
|
||||
</div>
|
||||
)}
|
||||
<span className="truncate font-medium text-sm">
|
||||
{agentName || agentInfo?.name || ""}
|
||||
</span>
|
||||
</div>
|
||||
<span className="shrink-0 text-xs text-muted-foreground">{triggerLabel}</span>
|
||||
{/* Accountable member (MUL-4302 §9): whose behalf this run is on. */}
|
||||
<AttributionBadge
|
||||
attribution={task.attribution}
|
||||
variant="inline"
|
||||
className="min-w-0"
|
||||
/>
|
||||
</div>
|
||||
{/* Accountable member (MUL-4302 §9): whose behalf this run is on. */}
|
||||
<AttributionBadge
|
||||
attribution={task.attribution}
|
||||
variant="inline"
|
||||
className="min-w-0"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="ml-auto flex shrink-0 items-center justify-center rounded p-1 text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Facts line: quantitative run facts as plain dot-separated
|
||||
typography — borders belong to controls, not to information.
|
||||
Diagnostic details (hostname, workdir path) hide behind
|
||||
hover/copy affordances instead of occupying the line. */}
|
||||
<div className="flex flex-wrap items-center gap-x-1.5 gap-y-1 text-xs text-muted-foreground">
|
||||
{runtimeInfo?.provider && (
|
||||
<div className="flex shrink-0 items-center gap-0.5">
|
||||
{hasRunDetails && (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
aria-label={t(($) => $.transcript.run_info)}
|
||||
title={t(($) => $.transcript.run_info)}
|
||||
className="flex h-7 w-7 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<Info className="h-3.5 w-3.5" />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="w-80 max-w-[calc(100vw-2rem)] p-3">
|
||||
<div className="mb-2 text-xs font-medium text-foreground">
|
||||
{t(($) => $.transcript.run_info)}
|
||||
</div>
|
||||
<div className="space-y-1 text-xs">
|
||||
{runtimeInfo && (
|
||||
<RunDetailRow label={t(($) => $.transcript.details_runtime)} value={runtimeInfo.name} />
|
||||
)}
|
||||
{providerLabel && (
|
||||
<RunDetailRow label={t(($) => $.transcript.details_provider)} value={providerLabel} />
|
||||
)}
|
||||
{runtimeInfo && (
|
||||
<RunDetailRow label={t(($) => $.transcript.details_mode)} value={runtimeInfo.runtime_mode} />
|
||||
)}
|
||||
{task.relative_work_dir && (
|
||||
<RunDetailRow
|
||||
label={t(($) => $.transcript.details_workdir)}
|
||||
value={task.relative_work_dir}
|
||||
mono
|
||||
onCopy={handleCopyWorkdir}
|
||||
copied={copiedWorkdir}
|
||||
copyTitle={t(($) => $.transcript.copy_workdir)}
|
||||
/>
|
||||
)}
|
||||
{createdLabel && (
|
||||
<RunDetailRow label={t(($) => $.transcript.details_created)} value={createdLabel} />
|
||||
)}
|
||||
{startedLabel && (
|
||||
<RunDetailRow label={t(($) => $.transcript.details_started)} value={startedLabel} />
|
||||
)}
|
||||
{completedLabel && (
|
||||
<RunDetailRow label={t(($) => $.transcript.details_completed)} value={completedLabel} />
|
||||
)}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── List toolbar: read-before-you-read summary (left) + controls
|
||||
(right). Duration + event count fill the left, so the row balances
|
||||
instead of leaving dead space. ── */}
|
||||
<div className="flex items-center gap-3 border-b px-4 py-1.5 shrink-0">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-x-1.5 overflow-hidden whitespace-nowrap text-xs text-muted-foreground">
|
||||
{duration && (
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<Cpu className="h-3 w-3" />
|
||||
{formatProvider(runtimeInfo.provider)}
|
||||
<Clock className="h-3 w-3" />
|
||||
{duration}
|
||||
</span>
|
||||
)}
|
||||
{runtimeInfo && (
|
||||
<>
|
||||
<FactDot />
|
||||
<span className="inline-flex items-center gap-1" title={runtimeInfo.name}>
|
||||
{runtimeInfo.runtime_mode === "cloud" ? (
|
||||
<Cloud className="h-3 w-3" />
|
||||
) : (
|
||||
<Monitor className="h-3 w-3" />
|
||||
)}
|
||||
{runtimeInfo.runtime_mode}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{duration && (
|
||||
<>
|
||||
<FactDot />
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
{duration}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{duration && <FactDot />}
|
||||
<span>
|
||||
{activeFilterKeys.length > 0
|
||||
? t(($) => $.transcript.events_filtered, { shown: filteredItems.length, total: items.length })
|
||||
: t(($) => $.transcript.events, { count: items.length })}
|
||||
</span>
|
||||
{toolCount > 0 && (
|
||||
<>
|
||||
<FactDot />
|
||||
<span>{t(($) => $.transcript.tool_calls, { count: toolCount })}</span>
|
||||
</>
|
||||
)}
|
||||
<FactDot />
|
||||
<span>
|
||||
{activeFilterKeys.length > 0
|
||||
? t(($) => $.transcript.events_filtered, { shown: filteredItems.length, total: items.length })
|
||||
: t(($) => $.transcript.events, { count: items.length })}
|
||||
</span>
|
||||
{task.created_at && (
|
||||
<>
|
||||
<FactDot />
|
||||
<span>
|
||||
{new Date(task.created_at).toLocaleString(undefined, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{/* Working directory — server-derived display path. Only
|
||||
`relative_work_dir` is safe to render / put in title / copy:
|
||||
the server has already stripped $HOME and the username out of
|
||||
it, while the absolute `task.work_dir` never reaches the DOM.
|
||||
The path itself stays out of the line (routinely long); the
|
||||
icon copies it and the tooltip shows it. */}
|
||||
{task.relative_work_dir && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyWorkdir}
|
||||
title={task.relative_work_dir}
|
||||
className="inline-flex items-center gap-1 rounded p-0.5 text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
{copiedWorkdir ? (
|
||||
<Check className="h-3 w-3 shrink-0 text-success" />
|
||||
) : (
|
||||
<Folder className="h-3 w-3 shrink-0" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── List toolbar: every control operating the list below ── */}
|
||||
<div className="flex flex-wrap items-center justify-end gap-1 border-b px-4 py-1.5 shrink-0">
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
{items.length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
@@ -643,6 +760,7 @@ export function AgentTranscriptDialog({
|
||||
{copied ? <Check className="h-3 w-3" /> : <Copy className="h-3 w-3" />}
|
||||
<span className="hidden sm:inline">{copyTranscriptLabel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Timeline progress bar ─────────────────────────────── */}
|
||||
|
||||
@@ -88,7 +88,7 @@ const SHELL_WRAPPER_PATTERN =
|
||||
|
||||
export function stripShellWrapper(command: string): string {
|
||||
const match = SHELL_WRAPPER_PATTERN.exec(command.trim());
|
||||
return match ? match[2] : command;
|
||||
return match?.[2] ?? command;
|
||||
}
|
||||
|
||||
function clip(value: string, max: number): string {
|
||||
|
||||
@@ -788,7 +788,27 @@
|
||||
"density_collapsed": "Collapse all",
|
||||
"density_collapsed_desc": "One line per event — the most compact scan",
|
||||
"no_output": "No output",
|
||||
"show_all": "Show all"
|
||||
"show_all": "Show all",
|
||||
"status_queued": "Queued",
|
||||
"status_dispatched": "Dispatched",
|
||||
"status_cancelled": "Cancelled",
|
||||
"status_waiting": "Waiting",
|
||||
"trigger_initial": "Initial run",
|
||||
"trigger_comment": "From a comment",
|
||||
"trigger_autopilot": "Autopilot",
|
||||
"trigger_retry": "Retry",
|
||||
"trigger_chat": "From chat",
|
||||
"trigger_quick_create": "Quick create",
|
||||
"trigger_direct": "Direct assignment",
|
||||
"run_info": "Run details",
|
||||
"details_runtime": "Runtime",
|
||||
"details_provider": "Provider",
|
||||
"details_mode": "Mode",
|
||||
"details_workdir": "Workdir",
|
||||
"details_created": "Created",
|
||||
"details_started": "Started",
|
||||
"details_completed": "Completed",
|
||||
"copy_workdir": "Copy working directory"
|
||||
},
|
||||
"task_failure": {
|
||||
"agent_error": "Agent execution error",
|
||||
|
||||
@@ -668,7 +668,27 @@
|
||||
"density_collapsed": "すべて折りたたむ",
|
||||
"density_collapsed_desc": "1 行 1 イベントの最もコンパクトな表示",
|
||||
"no_output": "出力なし",
|
||||
"show_all": "すべて表示"
|
||||
"show_all": "すべて表示",
|
||||
"status_queued": "待機列",
|
||||
"status_dispatched": "ディスパッチ済み",
|
||||
"status_cancelled": "キャンセル",
|
||||
"status_waiting": "待機中",
|
||||
"trigger_initial": "初回実行",
|
||||
"trigger_comment": "コメントから",
|
||||
"trigger_autopilot": "Autopilot",
|
||||
"trigger_retry": "再試行",
|
||||
"trigger_chat": "チャットから",
|
||||
"trigger_quick_create": "クイック作成",
|
||||
"trigger_direct": "直接アサイン",
|
||||
"run_info": "実行の詳細",
|
||||
"details_runtime": "ランタイム",
|
||||
"details_provider": "プロバイダー",
|
||||
"details_mode": "モード",
|
||||
"details_workdir": "作業ディレクトリ",
|
||||
"details_created": "作成",
|
||||
"details_started": "開始",
|
||||
"details_completed": "完了",
|
||||
"copy_workdir": "作業ディレクトリをコピー"
|
||||
},
|
||||
"task_failure": {
|
||||
"agent_error": "エージェント実行エラー",
|
||||
|
||||
@@ -676,7 +676,27 @@
|
||||
"density_collapsed": "모두 접기",
|
||||
"density_collapsed_desc": "이벤트당 한 줄, 가장 간결한 보기",
|
||||
"no_output": "출력 없음",
|
||||
"show_all": "모두 보기"
|
||||
"show_all": "모두 보기",
|
||||
"status_queued": "대기열",
|
||||
"status_dispatched": "디스패치됨",
|
||||
"status_cancelled": "취소됨",
|
||||
"status_waiting": "대기 중",
|
||||
"trigger_initial": "최초 실행",
|
||||
"trigger_comment": "댓글에서",
|
||||
"trigger_autopilot": "Autopilot",
|
||||
"trigger_retry": "재시도",
|
||||
"trigger_chat": "채팅에서",
|
||||
"trigger_quick_create": "빠른 생성",
|
||||
"trigger_direct": "직접 지정",
|
||||
"run_info": "실행 세부정보",
|
||||
"details_runtime": "런타임",
|
||||
"details_provider": "제공자",
|
||||
"details_mode": "모드",
|
||||
"details_workdir": "작업 디렉터리",
|
||||
"details_created": "생성",
|
||||
"details_started": "시작",
|
||||
"details_completed": "완료",
|
||||
"copy_workdir": "작업 디렉터리 복사"
|
||||
},
|
||||
"task_failure": {
|
||||
"agent_error": "에이전트 실행 오류",
|
||||
|
||||
@@ -772,7 +772,27 @@
|
||||
"density_collapsed": "全部收起",
|
||||
"density_collapsed_desc": "一行一事件,最紧凑的扫读视图",
|
||||
"no_output": "无输出",
|
||||
"show_all": "显示全部"
|
||||
"show_all": "显示全部",
|
||||
"status_queued": "排队中",
|
||||
"status_dispatched": "已派发",
|
||||
"status_cancelled": "已取消",
|
||||
"status_waiting": "等待中",
|
||||
"trigger_initial": "初次运行",
|
||||
"trigger_comment": "评论触发",
|
||||
"trigger_autopilot": "Autopilot",
|
||||
"trigger_retry": "重试",
|
||||
"trigger_chat": "来自对话",
|
||||
"trigger_quick_create": "快速创建",
|
||||
"trigger_direct": "直接指派",
|
||||
"run_info": "运行详情",
|
||||
"details_runtime": "运行时",
|
||||
"details_provider": "提供方",
|
||||
"details_mode": "模式",
|
||||
"details_workdir": "工作目录",
|
||||
"details_created": "创建",
|
||||
"details_started": "开始",
|
||||
"details_completed": "完成",
|
||||
"copy_workdir": "复制工作目录"
|
||||
},
|
||||
"task_failure": {
|
||||
"agent_error": "智能体执行出错",
|
||||
|
||||
Reference in New Issue
Block a user