From f64a9b76868c224c0a0bf066f37280dcd0f02137 Mon Sep 17 00:00:00 2001 From: song Date: Thu, 15 Oct 2026 22:40:53 +0800 Subject: [PATCH 1/6] feat(workspace): diagnose Agent runtime readiness in place A fresh install without an Agent CLI on PATH showed Codex, Claude Code and Kiro CLI only as disabled picker entries, with no reason, no next step and no way to confirm a repair short of reloading. The first-use journey asks the Dashboard to diagnose tool readiness in place and offer a concrete repair. Built-in endpoint rows now name the probe they failed: `unavailable_reason` "executable_not_found" with the probed `executable`, or "api_key_not_set" with the `environment_variable`; available rows carry the name without a reason. The frontend derives one typed readiness from the execution runtimes only (the read-only status projection and the loading placeholder never count): nothing available, the selected runtime unavailable here, or ready. The manager home shows a notice naming each missing command or variable with Check again and a Setup guide link; a Goal or manager conversation shows the same above the composer. Check again re-reads the service's runtime catalog and withdraws the notice only when it reports an available runtime. Nothing is installed, written or granted. Three hard-coded Chinese agent labels move to i18n on the way. Validation: Python readiness test and the chat test families; node test for the derivation; browser scenario covering the home notice with exact names, re-check withdrawal, the composer line and no Turn sent; agent-model-selection and steward-journey unchanged; contract test pins the detecting rule and the projection/placeholder exclusion. Co-Authored-By: Claude Fable 5.1 Signed-off-by: song --- apps/presentation/dashboard/package.json | 6 +- apps/presentation/dashboard/src/data/chat.ts | 4 ++ .../src/features/personal-workspace/i18n.tsx | 32 +++++++++ .../personal-workspace-contract.test.mjs | 13 ++++ .../personal-workspace-model.ts | 6 ++ .../personal-workspace-page.tsx | 61 ++++++++++++++++++ .../personal-workspace/personal-workspace.css | 11 ++++ .../runtime-readiness.test.mjs | 39 +++++++++++ .../personal-workspace/runtime-readiness.ts | 46 +++++++++++++ .../dashboard/src/views/dashboard-page.tsx | 21 +++++- .../runtime-readiness-none.png | Bin 0 -> 151155 bytes docs/guides/personal-workspace-user-guide.md | 18 ++++++ examples/personal-workspace-browser-smoke.mjs | 2 + .../personal-workspace-browser/fixture.mjs | 4 +- .../runtime-readiness.mjs | 60 +++++++++++++++++ loopx/chat_endpoint_catalog.py | 36 +++++++++-- tests/test_chat_endpoint_readiness.py | 41 ++++++++++++ 17 files changed, 387 insertions(+), 13 deletions(-) create mode 100644 apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.test.mjs create mode 100644 apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.ts create mode 100644 docs/assets/personal-workspace/runtime-readiness-none.png create mode 100644 examples/personal-workspace-browser/runtime-readiness.mjs create mode 100644 tests/test_chat_endpoint_readiness.py diff --git a/apps/presentation/dashboard/package.json b/apps/presentation/dashboard/package.json index 7412a42580..1aef33867e 100644 --- a/apps/presentation/dashboard/package.json +++ b/apps/presentation/dashboard/package.json @@ -38,10 +38,10 @@ "smoke:home-route": "rm -rf /tmp/loopx-home-route-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-home-route-smoke smoke/home-route-smoke.ts && node /tmp/loopx-home-route-smoke/home-route-smoke.js", "smoke:delegation-preflight": "rm -rf node_modules/.cache/loopx-delegation-preflight-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --jsx react-jsx --skipLibCheck --strict --rootDir . --outDir node_modules/.cache/loopx-delegation-preflight-smoke smoke/delegation-preflight-smoke.tsx src/data/delegation-preflight.ts src/features/personal-workspace/delegation-preflight-status.tsx && node node_modules/.cache/loopx-delegation-preflight-smoke/smoke/delegation-preflight-smoke.js", "smoke:delegation-preflight-browser": "node --experimental-strip-types smoke/delegation-preflight-browser-smoke.mjs", - "smoke:personal-workspace": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs", + "smoke:personal-workspace": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/runtime-readiness.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs", "smoke:workspace-locale": "LOOPX_PERSONAL_WORKSPACE_SCENARIO=workspace-locale node ../../../examples/personal-workspace-browser-smoke.mjs", - "smoke:personal-workspace-packaged": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser/server-startup.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-evidence-return-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-feedback-recovery-smoke.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/goal-workspace-craft-browser-smoke.mjs", - "smoke:personal-workspace-packaged:catalog": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser/server-startup.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs", + "smoke:personal-workspace-packaged": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/runtime-readiness.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser/server-startup.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-evidence-return-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-feedback-recovery-smoke.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/goal-workspace-craft-browser-smoke.mjs", + "smoke:personal-workspace-packaged:catalog": "npm run smoke:conversation-history-order && npm run smoke:goal-order && npm run smoke:goal-activity && npm run smoke:proposal-recency && node src/features/personal-workspace/conversation-order.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/steering-recovery.test.mjs && node --no-warnings --experimental-strip-types --test src/features/personal-workspace/runtime-readiness.test.mjs && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/font-token.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser/server-startup.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs", "smoke:personal-workspace-packaged:team": "LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-evidence-return-smoke.mjs && LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/team-feedback-recovery-smoke.mjs", "smoke:personal-workspace-packaged:craft": "LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node smoke/goal-workspace-craft-browser-smoke.mjs", "smoke:todo-resume-condition": "rm -rf /tmp/loopx-todo-resume-condition-smoke && tsc --ignoreConfig --target ES2022 --module commonjs --moduleResolution node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-todo-resume-condition-smoke smoke/todo-resume-condition-smoke.ts src/features/personal-workspace/todo-resume-condition.ts && node /tmp/loopx-todo-resume-condition-smoke/smoke/todo-resume-condition-smoke.js", diff --git a/apps/presentation/dashboard/src/data/chat.ts b/apps/presentation/dashboard/src/data/chat.ts index fe40fe4bdf..08f563066b 100644 --- a/apps/presentation/dashboard/src/data/chat.ts +++ b/apps/presentation/dashboard/src/data/chat.ts @@ -205,6 +205,10 @@ export const chatCapabilitiesSchema = z.object({ source: z.string().optional(), tool_calls: z.boolean().optional(), trust_scope: z.string().optional(), + // Readiness: why an unavailable built-in runtime is unavailable and what it needs. + unavailable_reason: z.string().nullable().optional(), + executable: z.string().nullable().optional(), + environment_variable: z.string().nullable().optional(), })).optional(), lark_cli: z.object({ available: z.boolean(), diff --git a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx index 4db768d258..f06a409984 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx @@ -576,6 +576,22 @@ const en = { "files.title": "Files & Outputs", "files.verifiedReport": "Verified milestone report", "header.agentUnavailable": "Unavailable", + "agent.statusAvailable": "Available", + "agent.statusDetecting": "Detecting…", + "agent.statusNeedsSetup": "Needs setup", + "agent.workspaceWriteVerified": "Verified before this Goal writes", + "agent.workspaceUnavailable": "Unavailable; repair the runtime first", + "readiness.noneTitle": "No Agent runtime is ready on this machine", + "readiness.noneLead": "LoopX drives Agents through a command-line tool installed here, such as Codex, Claude Code or Kiro CLI, or through an API key. Install and sign in to one, then check again. Nothing is installed or granted from this page.", + "readiness.missingExecutable": "{label}: command “{name}” was not found on PATH", + "readiness.missingEnvironment": "{label}: environment variable {name} is not set", + "readiness.missingOther": "{label}: not available ({reason})", + "readiness.missingUnknown": "{label}: not available", + "readiness.selectedUnavailable": "{label} is not available on this machine. Install and sign in, then check again, or pick another runtime.", + "readiness.selectedUnavailableNone": "No Agent runtime is ready on this machine, so messages cannot be sent yet.", + "readiness.recheck": "Check again", + "readiness.rechecking": "Checking…", + "readiness.guide": "Setup guide", "header.chatRuntime": "Chat", "header.workAgentCount": "{count} work Agents", "header.overview": "Overview", @@ -2083,6 +2099,22 @@ const zhCN: Record = { "files.title": "Files & Outputs", "files.verifiedReport": "已验证阶段周报", "header.agentUnavailable": "不可用", + "agent.statusAvailable": "可用", + "agent.statusDetecting": "正在检测", + "agent.statusNeedsSetup": "需要配置", + "agent.workspaceWriteVerified": "当前 Goal 写入前验证", + "agent.workspaceUnavailable": "不可用,需先修复运行环境", + "readiness.noneTitle": "这台机器上还没有可用的 Agent 运行环境", + "readiness.noneLead": "LoopX 通过本机已安装的命令行工具驱动 Agent,例如 Codex、Claude Code 或 Kiro CLI,也可以通过 API 密钥。安装并登录其中一个后,点「重新检查」。本页不会安装任何东西,也不授予任何权限。", + "readiness.missingExecutable": "{label}:未在 PATH 中找到命令「{name}」", + "readiness.missingEnvironment": "{label}:未设置环境变量 {name}", + "readiness.missingOther": "{label}:不可用({reason})", + "readiness.missingUnknown": "{label}:不可用", + "readiness.selectedUnavailable": "{label} 在这台机器上不可用。安装并登录后重新检查,或选择其他运行环境。", + "readiness.selectedUnavailableNone": "这台机器上还没有可用的 Agent 运行环境,暂时无法发送消息。", + "readiness.recheck": "重新检查", + "readiness.rechecking": "正在检查…", + "readiness.guide": "安装指引", "header.chatRuntime": "Chat", "header.workAgentCount": "{count} 个工作 Agent", "header.overview": "概览", diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs index 2efa09d6c0..a769280453 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs @@ -600,4 +600,17 @@ for (const key of ["home.firstGoal.title", "home.firstGoal.lead", "home.firstGoa assert.equal((i18n.match(new RegExp(`"${key.replace(/\./g, "\\.")}":`, "g")) ?? []).length, 2, `${key} exists in both locales`); } +// Runtime readiness: the service's own reason fields drive the copy; the +// read-only status projection and the loading placeholder never count as a +// runtime; a re-check reads the catalog again and nothing is installed. +const runtimeReadiness = source("./runtime-readiness.ts"); +assert.match(runtimeReadiness, /if \(agents\.length === 0\) return \{ kind: "detecting" \};/, "An empty catalog is detecting, not absence"); +assert.match(page, /agent\.adapterKind !== "status_projection" && agent\.source !== "detecting"/, "Readiness ignores the status projection and the loading placeholder"); +assert.match(dashboard, /onRecheckRuntimes: \(\) => setCapabilityRevision\(\(revision\) => revision \+ 1\)/, "Re-check re-reads the capabilities catalog"); +assert.match(dashboard, /source: "detecting",/, "The placeholder row is typed as detecting"); +assert.doesNotMatch(dashboard, /statusLabel: "正在检测"|"可用" : "需要配置"|当前 Goal 写入前验证/, "Agent status labels are localized"); +for (const key of ["readiness.noneTitle", "readiness.missingExecutable", "readiness.missingEnvironment", "readiness.selectedUnavailable", "readiness.recheck", "agent.statusDetecting"]) { + assert.equal((i18n.match(new RegExp(`"${key.replace(/\./g, "\\.")}":`, "g")) ?? []).length, 2, `${key} exists in both locales`); +} + console.log("personal workspace drawer contract smoke passed"); diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-model.ts b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-model.ts index 8569de6346..ee89f9d6ef 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-model.ts +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-model.ts @@ -483,6 +483,10 @@ export type WorkspaceAgentOption = { agentId: string; available: boolean; capability?: string; + /** Service-reported readiness for an unavailable runtime; see runtime-readiness.ts. */ + unavailableReason?: string | null; + executable?: string | null; + environmentVariable?: string | null; interrupt?: boolean; label: string; location?: string; @@ -569,6 +573,8 @@ export type PersonalWorkspaceCallbacks = { onReconcileStatus?: (options?: { invalidateGoalIds?: string[] }) => void | Promise; /** Full refresh by default; error recovery can explicitly read missing Goals only. */ onRefresh?: (scope?: WorkspaceReadScope) => void | WorkspaceRefreshResult | Promise; + /** Re-read the Chat service's runtime catalog after the owner repaired something. */ + onRecheckRuntimes?: () => void; onRetryGoalArchive?: () => void | Promise; onPreviewAction?: (request: WorkspaceActionPreviewRequest) => WorkspaceActionPreview | Promise; onRequestGoalCreate?: () => WorkspaceActionPreview | Promise | void; diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx index d982251735..864a30fe63 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx @@ -88,6 +88,7 @@ import { WorkspaceSettingsPage } from "./workspace-settings-page"; import { UsageStatisticsNotice } from "./usage-statistics-notice"; import { readWorkspaceTheme, writeWorkspaceTheme, type WorkspaceTheme } from "./workspace-theme"; import { compareProposalRecency } from "./proposal-recency"; +import { deriveRuntimeReadiness, runtimeRequirement, runtimeSetupGuideUrl, type RuntimeAgentReadiness, type RuntimeReadiness } from "./runtime-readiness"; import { WorkspaceShell } from "./workspace-shell"; import type { StatusSourceControl } from "./status-source-switcher"; import "./personal-workspace.css"; @@ -117,6 +118,30 @@ function activityTimeLabel(value: string | undefined, locale: string, t: Workspa return new Intl.DateTimeFormat(locale, { month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit", hour12: false }).format(parsed); } +function runtimeMissingLine(agent: RuntimeAgentReadiness, t: WorkspaceTranslate): string { + const requirement = runtimeRequirement(agent); + if (requirement.kind === "executable") return t("readiness.missingExecutable", { label: agent.label, name: requirement.name }); + if (requirement.kind === "environment_variable") return t("readiness.missingEnvironment", { label: agent.label, name: requirement.name }); + return requirement.reason ? t("readiness.missingOther", { label: agent.label, reason: requirement.reason }) : t("readiness.missingUnknown", { label: agent.label }); +} + +/** The home's in-place readiness notice: what is missing, where to set it up, and a re-check that reads back. */ +function RuntimeReadinessNotice({ readiness, rechecking, onRecheck }: { readiness: RuntimeReadiness; rechecking: boolean; onRecheck?: () => void }) { + const { t } = useWorkspaceI18n(); + if (readiness.kind !== "none_available") return null; + return ( +
+

{t("readiness.noneTitle")}

+

{t("readiness.noneLead")}

+
    {readiness.missing.map((agent) =>
  • {runtimeMissingLine(agent, t)}
  • )}
+
+ {onRecheck ? : null} + {t("readiness.guide")} +
+
+ ); +} + function ManagerHomeBoard({ goals, onSelectGoal, @@ -125,6 +150,9 @@ function ManagerHomeBoard({ onboarding, onRequestGoalCreate, onSuggestRequest, + runtimeReadiness, + recheckingRuntimes, + onRecheckRuntimes, systemHealth, operations, onSelectOperation, @@ -138,6 +166,9 @@ function ManagerHomeBoard({ onboarding?: WorkspaceHomeOnboarding; onRequestGoalCreate?: () => void; onSuggestRequest?: (text: string) => void; + runtimeReadiness?: RuntimeReadiness; + recheckingRuntimes?: boolean; + onRecheckRuntimes?: () => void; systemHealth?: WorkspaceSystemHealth; operations: WorkspaceActionPreview[]; onSelectOperation: (proposal: WorkspaceActionPreview) => void; @@ -172,6 +203,7 @@ function ManagerHomeBoard({ ); return (
+ {runtimeReadiness ? : null} {systemHealth && (!systemHealth.ok || systemHealth.issues.length > 0 || systemHealth.freshnessWarning) ? (
@@ -988,6 +1020,27 @@ export function PersonalWorkspacePage({ const selectedGoalId = controlledGoalId === undefined ? localGoalId : controlledGoalId; const actionReadback = useTypedActionReadback(readOnly, selectedGoalId); const selectedAgentId = controlledAgentId ?? localAgentId; + // Only execution runtimes count: the read-only status projection answers + // questions but runs nothing, and the loading placeholder is not a fact. + const runtimeReadiness = useMemo(() => deriveRuntimeReadiness(agents + .filter((agent) => agent.adapterKind !== "status_projection" && agent.source !== "detecting") + .map((agent) => ({ + agentId: agent.agentId, label: agent.label, available: agent.available, + unavailableReason: agent.unavailableReason ?? null, executable: agent.executable ?? null, environmentVariable: agent.environmentVariable ?? null, + })), selectedAgentId), [agents, selectedAgentId]); + // A re-check reads the runtime catalog again; the answer arrives as new agents. + const [recheckingRuntimes, setRecheckingRuntimes] = useState(false); + useEffect(() => { setRecheckingRuntimes(false); }, [agents]); + useEffect(() => { + if (!recheckingRuntimes) return; + const timer = window.setTimeout(() => setRecheckingRuntimes(false), 8_000); + return () => window.clearTimeout(timer); + }, [recheckingRuntimes]); + function recheckRuntimes() { + if (!callbacks.onRecheckRuntimes) return; + setRecheckingRuntimes(true); + callbacks.onRecheckRuntimes(); + } const conversationKey = selectedWorkspaceRef ? conversationContextKey({ kind: "project", projectRef: selectedWorkspaceRef }) : selectedGoalId ?? "manager"; @@ -2325,6 +2378,7 @@ export function PersonalWorkspacePage({ ) : !managerChatOpen ? ( void callbacks.onRefresh?.("missing")} onRetrySource={() => void refreshWorkspace()} onSelectGoal={selectGoal} systemHealth={model.systemHealth} onboarding={model.onboarding} onRequestGoalCreate={readOnly ? undefined : requestGoalCreate} onSuggestRequest={readOnly ? undefined : suggestReply} + runtimeReadiness={readOnly ? undefined : runtimeReadiness} recheckingRuntimes={recheckingRuntimes} onRecheckRuntimes={callbacks.onRecheckRuntimes ? recheckRuntimes : undefined} operations={actionReadback.isError ? [] : homeOperations} onSelectOperation={proposal => setSelection({kind: "proposal", item: proposal})} onViewAllOperations={() => setSelectedGoalTab("chat")} /> ) : ( @@ -2427,6 +2481,13 @@ export function PersonalWorkspacePage({ ))}
: null} {imageAttachmentError ?

{imageAttachmentError}

: null} + {!readOnly && (selectedGoal || managerChatOpen) && (runtimeReadiness.kind === "none_available" || runtimeReadiness.kind === "selected_unavailable") ? ( +

+ {runtimeReadiness.kind === "none_available" ? t("readiness.selectedUnavailableNone") : t("readiness.selectedUnavailable", { label: runtimeReadiness.selected.label })} + {callbacks.onRecheckRuntimes ? : null} + {t("readiness.guide")} +

+ ) : null} {conversationTurnRunning ?

{steeringTurnId ? (locale === "zh-CN" ? "本轮进行中 · 发消息可调整当前工作" : "Turn in progress · send instructions to adjust this work") : t("composer.turnRunning")}

: null} diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css index d4172394a9..bb531a884b 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css @@ -332,6 +332,17 @@ button.personal-execution-chip:focus-visible { outline: 2px solid #0070f3; outli .personal-system-health-diagnostics { margin-top: 6px; font-size: var(--text-caption); line-height: var(--leading-caption); } .personal-system-health-diagnostics summary { cursor: pointer; } .personal-system-health-diagnostics ul { margin: 4px 0 0 16px; padding: 0; } +.personal-runtime-readiness { display: grid; gap: 10px; max-width: 640px; min-width: 0; padding: 14px 16px; border: 1px solid var(--pw-amber); border-radius: 12px; background: var(--pw-amber-bg); color: var(--pw-text); } +.personal-runtime-readiness h2 { margin: 0; font-size: var(--text-body); line-height: var(--leading-body); font-weight: 600; } +.personal-runtime-readiness p { margin: 0; color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); } +.personal-runtime-readiness ul { margin: 0; padding-left: 18px; font-size: var(--text-caption); line-height: var(--leading-caption); } +.personal-runtime-readiness-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; } +.personal-runtime-readiness-actions button { min-height: 36px; padding: 0 14px; border: 1px solid var(--pw-amber); border-radius: 8px; background: var(--pw-card); color: var(--pw-amber); font-size: var(--text-caption); font-weight: 600; cursor: pointer; } +.personal-runtime-readiness-actions button:disabled { opacity: .6; cursor: wait; } +.personal-runtime-readiness-actions a { color: var(--pw-blue-ink); font-size: var(--text-caption); line-height: var(--leading-caption); } +.personal-runtime-readiness-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; color: var(--pw-amber); } +.personal-runtime-readiness-line button { min-height: 28px; padding: 0 10px; border: 1px solid var(--pw-amber); border-radius: 6px; background: transparent; color: var(--pw-amber); font-size: var(--text-caption); cursor: pointer; } +.personal-runtime-readiness-line a { color: var(--pw-blue-ink); } .personal-home-lane { display: flex; flex-direction: column; min-width: 0; min-height: 100%; border: 1px solid var(--pw-line); border-radius: 16px; background: #fff; box-shadow: 0 1px 4px rgb(30 28 20 / 4%); } .personal-manager-greeting > span { display: grid; flex: none; place-items: center; width: 44px; height: 44px; border-radius: 12px; background: var(--pw-blue-soft); color: var(--pw-blue); } .personal-manager-greeting div { display: grid; gap: 4px; min-width: 0; } diff --git a/apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.test.mjs b/apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.test.mjs new file mode 100644 index 0000000000..2cf166adcc --- /dev/null +++ b/apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.test.mjs @@ -0,0 +1,39 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { deriveRuntimeReadiness, runtimeRequirement } from "./runtime-readiness.ts"; + +const codex = { agentId: "codex", label: "Codex", available: true, executable: "codex" }; +const codexMissing = { ...codex, available: false, unavailableReason: "executable_not_found" }; +const claudeMissing = { agentId: "claude-code", label: "Claude Code", available: false, unavailableReason: "executable_not_found", executable: "claude" }; +const apiMissing = { agentId: "anthropic-api", label: "Claude API", available: false, unavailableReason: "api_key_not_set", environmentVariable: "ANTHROPIC_API_KEY" }; + +test("an empty list is still detecting, never evidence of absence", () => { + assert.deepEqual(deriveRuntimeReadiness([], "codex"), { kind: "detecting" }); +}); + +test("nothing available lists every runtime as missing", () => { + const readiness = deriveRuntimeReadiness([codexMissing, claudeMissing, apiMissing], "codex"); + assert.equal(readiness.kind, "none_available"); + assert.deepEqual(readiness.missing.map((row) => row.agentId), ["codex", "claude-code", "anthropic-api"]); +}); + +test("a selected runtime that is not available names the alternatives", () => { + const readiness = deriveRuntimeReadiness([codexMissing, { ...claudeMissing, available: true }], "codex"); + assert.equal(readiness.kind, "selected_unavailable"); + assert.equal(readiness.selected.agentId, "codex"); + assert.deepEqual(readiness.alternatives.map((row) => row.agentId), ["claude-code"]); +}); + +test("an available selection, or no selection with something available, is ready", () => { + assert.deepEqual(deriveRuntimeReadiness([codex, claudeMissing], "codex"), { kind: "ready", selected: codex }); + assert.deepEqual(deriveRuntimeReadiness([codex, claudeMissing], null), { kind: "ready", selected: null }); + assert.deepEqual(deriveRuntimeReadiness([codex], "unknown-agent"), { kind: "ready", selected: null }, "an unknown selection does not block"); +}); + +test("requirements are named from the service's reason, never guessed", () => { + assert.deepEqual(runtimeRequirement(codexMissing), { kind: "executable", name: "codex" }); + assert.deepEqual(runtimeRequirement(apiMissing), { kind: "environment_variable", name: "ANTHROPIC_API_KEY" }); + assert.deepEqual(runtimeRequirement({ ...claudeMissing, unavailableReason: "host offline", executable: null }), { kind: "other", reason: "host offline" }); + assert.deepEqual(runtimeRequirement({ ...claudeMissing, unavailableReason: null }), { kind: "other", reason: null }); +}); diff --git a/apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.ts b/apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.ts new file mode 100644 index 0000000000..5f57977025 --- /dev/null +++ b/apps/presentation/dashboard/src/features/personal-workspace/runtime-readiness.ts @@ -0,0 +1,46 @@ +/** + * Readiness of the Agent runtimes the Chat service can drive. + * + * Availability is the service's live probe. This module only classifies it for + * the owner: nothing is ready yet, the runtime they picked is not available + * here, or they can proceed. Unknown stays "detecting"; an empty list is not + * evidence that nothing is installed. + */ + +export type RuntimeAgentReadiness = { + agentId: string; + label: string; + available: boolean; + unavailableReason?: string | null; + executable?: string | null; + environmentVariable?: string | null; +}; + +export type RuntimeReadiness = + | { kind: "detecting" } + | { kind: "ready"; selected: RuntimeAgentReadiness | null } + | { kind: "selected_unavailable"; selected: RuntimeAgentReadiness; alternatives: RuntimeAgentReadiness[] } + | { kind: "none_available"; missing: RuntimeAgentReadiness[] }; + +export function deriveRuntimeReadiness(agents: readonly RuntimeAgentReadiness[], selectedAgentId: string | null | undefined): RuntimeReadiness { + if (agents.length === 0) return { kind: "detecting" }; + const available = agents.filter((agent) => agent.available); + if (available.length === 0) return { kind: "none_available", missing: [...agents] }; + const selected = agents.find((agent) => agent.agentId === selectedAgentId) ?? null; + if (selected && !selected.available) return { kind: "selected_unavailable", selected, alternatives: available }; + return { kind: "ready", selected }; +} + +/** What the owner has to supply for one runtime, in a form copy can name. */ +export type RuntimeRequirement = + | { kind: "executable"; name: string } + | { kind: "environment_variable"; name: string } + | { kind: "other"; reason: string | null }; + +export function runtimeRequirement(agent: RuntimeAgentReadiness): RuntimeRequirement { + if (agent.unavailableReason === "executable_not_found" && agent.executable) return { kind: "executable", name: agent.executable }; + if (agent.unavailableReason === "api_key_not_set" && agent.environmentVariable) return { kind: "environment_variable", name: agent.environmentVariable }; + return { kind: "other", reason: agent.unavailableReason ?? null }; +} + +export const runtimeSetupGuideUrl = "https://github.com/loopx-project/loopx/blob/main/docs/guides/getting-started.md#codex-app-and-other-agent-setup"; diff --git a/apps/presentation/dashboard/src/views/dashboard-page.tsx b/apps/presentation/dashboard/src/views/dashboard-page.tsx index 5877abf0dd..97a82cf533 100644 --- a/apps/presentation/dashboard/src/views/dashboard-page.tsx +++ b/apps/presentation/dashboard/src/views/dashboard-page.tsx @@ -553,6 +553,9 @@ type PersonalAgentOption = { agentId: string; available: boolean; capability: string; + unavailableReason?: string | null; + executable?: string | null; + environmentVariable?: string | null; interrupt?: boolean; label: string; location?: string; @@ -1254,6 +1257,9 @@ function PersonalGoalHome({ streaming: boolean; tool_calls?: boolean; trust_scope?: string; + unavailable_reason?: string | null; + executable?: string | null; + environment_variable?: string | null; }>>([]); const [goalSubagentConfigurationEnabled, setGoalSubagentConfigurationEnabled] = useState(false); const [managerRuntime, setManagerRuntime] = useState(null); @@ -1332,7 +1338,10 @@ function PersonalGoalHome({ location: agent.location, resume: agent.resume, source: agent.source, - statusLabel: agent.available ? "可用" : "需要配置", + statusLabel: agent.available ? t("agent.statusAvailable") : t("agent.statusNeedsSetup"), + unavailableReason: agent.unavailable_reason ?? null, + executable: agent.executable ?? null, + environmentVariable: agent.environment_variable ?? null, streaming: agent.streaming, toolCalls: agent.tool_calls, trustScope: agent.trust_scope, @@ -1342,7 +1351,9 @@ function PersonalGoalHome({ available: true, capability: personalAgentCapability("codex"), label: "Codex", - statusLabel: "正在检测", + // A placeholder while the catalog loads; readiness must not read it as a fact. + source: "detecting", + statusLabel: t("agent.statusDetecting"), }]; // The status projection answers about Goals; a workspace conversation needs an executor. const agentOptions = [ @@ -2748,7 +2759,10 @@ function PersonalGoalHome({ streaming: agent.streaming, toolCalls: agent.toolCalls, trustScope: agent.trustScope, - workspaceCompatibility: agent.available ? "当前 Goal 写入前验证" : "不可用,需先修复 Endpoint", + unavailableReason: agent.unavailableReason, + executable: agent.executable, + environmentVariable: agent.environmentVariable, + workspaceCompatibility: agent.available ? t("agent.workspaceWriteVerified") : t("agent.workspaceUnavailable"), }))} callbacks={{ onApplyAttention: (attention) => openGoalChat(attention.goalId), @@ -2977,6 +2991,7 @@ function PersonalGoalHome({ setCapabilityRevision((revision) => revision + 1); return result; }, + onRecheckRuntimes: () => setCapabilityRevision((revision) => revision + 1), onRetryResumeRun: retryManagerSession, onSelectAgent: chooseAgent, onSelectAgentModel: chooseAgentModel, diff --git a/docs/assets/personal-workspace/runtime-readiness-none.png b/docs/assets/personal-workspace/runtime-readiness-none.png new file mode 100644 index 0000000000000000000000000000000000000000..0519e022eb68864785513ccb7ff8dcaf60b28b27 GIT binary patch literal 151155 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

2>)=^k;l-fpZAZweV)X&x{#;ejaOvWN3f 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 z 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 G^M3%Yi}XVP literal 0 HcmV?d00001 diff --git a/docs/guides/personal-workspace-user-guide.md b/docs/guides/personal-workspace-user-guide.md index f0f95c9fbf..06cd80f16a 100644 --- a/docs/guides/personal-workspace-user-guide.md +++ b/docs/guides/personal-workspace-user-guide.md @@ -44,6 +44,24 @@ curl -fsS http://127.0.0.1:8767/status.json --- +### 1.1 Agent 运行环境未就绪 / When no Agent runtime is ready + +![没有可用 Agent 运行环境时的管家总览](../assets/personal-workspace/runtime-readiness-none.png) + +LoopX 通过本机已安装的命令行工具驱动 Agent(Codex、Claude Code、Kiro CLI),或通过 API 密钥。 +如果这台机器上一个都没有就绪,管家总览会在顶部说明原因,逐项列出缺什么(未在 PATH 中找到的命令、 +未设置的环境变量),并提供「重新检查」和「安装指引」。安装并登录后点「重新检查」,页面重新读取 +服务的运行环境目录并回读结果;本页不会安装任何东西,也不授予任何权限。打开 Goal 对话时,如果选中的 +运行环境在本机不可用,输入框上方会给出同样的说明和重新检查。 + +LoopX drives Agents through a command-line tool installed on this machine (Codex, Claude Code, +Kiro CLI) or through an API key. When none is ready, the manager overview says so at the top, +lists what each runtime needs (a command not found on PATH, an environment variable not set), and +offers **Check again** and a **Setup guide** link. After installing and signing in, **Check again** +re-reads the service's runtime catalog and reads the result back; the page installs nothing and +grants nothing. In a Goal conversation, the same notice and re-check appear above the composer when +the selected runtime is unavailable here. + ## 🧭 2. 控制台核心架构 ```mermaid diff --git a/examples/personal-workspace-browser-smoke.mjs b/examples/personal-workspace-browser-smoke.mjs index 4c5efde221..92fe3638fb 100644 --- a/examples/personal-workspace-browser-smoke.mjs +++ b/examples/personal-workspace-browser-smoke.mjs @@ -6,6 +6,7 @@ import {agentModelSelectionScenario} from "./personal-workspace-browser/agent-mo import {goalWorkspaceM2Scenario} from "./personal-workspace-browser/goal-workspace-m2.mjs"; import {goalWorkspaceSelectionScenario} from "./personal-workspace-browser/goal-workspace-selection.mjs"; import {firstGoalHomeScenario} from "./personal-workspace-browser/first-goal-home.mjs"; +import {runtimeReadinessScenario} from "./personal-workspace-browser/runtime-readiness.mjs"; import { replanCadenceScenario } from "./personal-workspace-browser/replan-cadence.mjs"; import {nativeChildActivityScenario} from "./personal-workspace-browser/native-child-activity.mjs"; import {privateStewardScopeScenario} from "./personal-workspace-browser/private-steward-scope.mjs"; @@ -111,6 +112,7 @@ scenarioCatalog.push(goalWorkspaceM2Scenario); scenarioCatalog.push(goalWorkspaceCreateScenario); scenarioCatalog.push(goalWorkspaceSelectionScenario); scenarioCatalog.push(firstGoalHomeScenario); +scenarioCatalog.push(runtimeReadinessScenario); const requestedScenario = process.env.LOOPX_PERSONAL_WORKSPACE_SCENARIO; scenarioCatalog.push(agentModelSelectionScenario); const scenarios = requestedScenario diff --git a/examples/personal-workspace-browser/fixture.mjs b/examples/personal-workspace-browser/fixture.mjs index 117aa6a8b8..2b8f4a2096 100644 --- a/examples/personal-workspace-browser/fixture.mjs +++ b/examples/personal-workspace-browser/fixture.mjs @@ -448,6 +448,8 @@ export async function installApi(page, { fixturePort = port, goalSubagentConfigu failNextStatusRequest: false, /** When set, every status read answers this HTTP status until cleared: a source that cannot be projected. */ statusFailure: null, + /** When set, replaces the capabilities adapters list: the runtimes this host reports. */ + runtimeAgentsOverride: null, freezeGoalSubagentStatusProjection: false, goalSubagentConfigurationEnabled, goalActivationStates: new Map([ @@ -1593,7 +1595,7 @@ export async function installApi(page, { fixturePort = port, goalSubagentConfigu ...(state.goalSubagentConfigurationEnabled ? { goal_subagent_configuration: "preview_locked" } : {}), goal_id: null, streaming: true, resume: true, interrupt: true, typed_actions: true, action_kinds: ["goal.create", "goal.lifecycle", "agent.bind", "heartbeat.bind", "monitor.create", "run.correct"], - adapters: [ + adapters: state.runtimeAgentsOverride ?? [ { agent_id: "codex", display_name: "Codex", adapter_kind: "codex_app_server", available: true, streaming: true, resume: true, interrupt: true }, { agent_id: "claude-code", display_name: "Claude Code", adapter_kind: "claude_code_cli", available: true, streaming: true, resume: true, interrupt: true }, { agent_id: "offline-agent", display_name: "Offline Agent", adapter_kind: "acp", available: false, streaming: false, resume: false, interrupt: false }, diff --git a/examples/personal-workspace-browser/runtime-readiness.mjs b/examples/personal-workspace-browser/runtime-readiness.mjs new file mode 100644 index 0000000000..17e7f7254c --- /dev/null +++ b/examples/personal-workspace-browser/runtime-readiness.mjs @@ -0,0 +1,60 @@ +import assert from "node:assert/strict"; +import { resolve } from "node:path"; +import { outputDir } from "./fixture.mjs"; +import { openWorkspacePage } from "./scenario-context.mjs"; + +// A machine with no Agent runtime must say so in place, name what each +// runtime needs, and let the owner re-check after installing, without the +// page installing or granting anything. The service's own readiness fields +// drive the copy; the home notice and the composer line share one rule. + +const missing = [ + { agent_id: "codex", display_name: "Codex", adapter_kind: "codex_app_server", available: false, streaming: true, resume: true, interrupt: true, executable: "codex", unavailable_reason: "executable_not_found" }, + { agent_id: "claude-code", display_name: "Claude Code", adapter_kind: "claude_code_cli", available: false, streaming: true, resume: true, interrupt: true, executable: "claude", unavailable_reason: "executable_not_found" }, + { agent_id: "anthropic-api", display_name: "Claude API", adapter_kind: "anthropic_messages_api", available: false, streaming: false, resume: true, interrupt: false, environment_variable: "ANTHROPIC_API_KEY", unavailable_reason: "api_key_not_set" }, +]; +const repaired = [{ ...missing[0], available: true, unavailable_reason: undefined }, missing[1], missing[2]]; + +export const runtimeReadinessScenario = { + id: "runtime-readiness", + async run({ browser, collectCoverage, url }) { + const session = await openWorkspacePage(browser, url, { collectCoverage, viewport: { width: 1440, height: 900 }, + beforeGoto: (api) => { api.runtimeAgentsOverride = missing; } }); + const { api, page } = session; + try { + const notice = page.getByTestId("runtime-readiness"); + await notice.waitFor({ state: "visible", timeout: 15_000 }); + const text = await notice.innerText(); + assert.match(text, /还没有可用的 Agent 运行环境/); + assert.match(text, /Codex:未在 PATH 中找到命令「codex」/, "The notice names the missing command from the service's fields"); + assert.match(text, /Claude API:未设置环境变量 ANTHROPIC_API_KEY/); + assert.equal(await notice.getByRole("link", { name: "安装指引" }).count(), 1); + await page.screenshot({ path: resolve(outputDir, "runtime-readiness-none.png"), animations: "disabled" }); + + // Re-check after the owner installed Codex: the notice withdraws and nothing was sent. + api.runtimeAgentsOverride = repaired; + await notice.getByRole("button", { name: "重新检查" }).click(); + await notice.waitFor({ state: "hidden", timeout: 15_000 }); + assert.equal(await page.getByTestId("runtime-readiness").count(), 0, "A repaired runtime clears the notice"); + + // With a Goal open and every runtime missing, the composer says why nothing can be sent. + api.runtimeAgentsOverride = missing; + await page.getByRole("button", { name: "刷新状态", exact: true }).click(); + await page.locator(".personal-goal-row").first().locator(".personal-goal-link").click(); + await page.getByTestId("goal-vitals").waitFor({ state: "visible", timeout: 15_000 }); + await page.getByRole("navigation", { name: "Goal 视图" }).getByRole("button", { name: "对话", exact: true }).click(); + const line = page.getByTestId("runtime-readiness-line"); + await line.waitFor({ state: "visible", timeout: 15_000 }); + assert.match(await line.innerText(), /还没有可用的 Agent 运行环境|不可用/); + await page.screenshot({ path: resolve(outputDir, "runtime-readiness-composer.png"), animations: "disabled" }); + api.runtimeAgentsOverride = repaired; + await line.getByRole("button", { name: "重新检查" }).click(); + await line.waitFor({ state: "hidden", timeout: 15_000 }); + assert.equal(api.turnRequests.length, 0, "Readiness never sends anything"); + return { coverageEntries: await session.close() }; + } catch (error) { + await session.close().catch(() => {}); + throw error; + } + }, +}; diff --git a/loopx/chat_endpoint_catalog.py b/loopx/chat_endpoint_catalog.py index 4b482ca298..4d0331f324 100644 --- a/loopx/chat_endpoint_catalog.py +++ b/loopx/chat_endpoint_catalog.py @@ -67,6 +67,24 @@ def managed_host_capability(runtime_root: Path | None = None) -> dict[str, Any]: } +def _executable_readiness(executable: str) -> dict[str, Any]: + """Live PATH probe for a CLI-backed endpoint, naming the missing command.""" + + if shutil.which(executable): + return {"available": True, "executable": executable} + return {"available": False, "executable": executable, + "unavailable_reason": "executable_not_found"} + + +def _environment_readiness(variable: str) -> dict[str, Any]: + """Presence probe for an API-key endpoint, naming the unset variable.""" + + if os.environ.get(variable, "").strip(): + return {"available": True, "environment_variable": variable} + return {"available": False, "environment_variable": variable, + "unavailable_reason": "api_key_not_set"} + + def builtin_chat_endpoints( *, codex_bin: str, @@ -74,14 +92,20 @@ def builtin_chat_endpoints( kiro_cli_bin: str, runtime_root: Path | None = None, ) -> list[dict[str, Any]]: - """Return the built-in endpoints, managed host included.""" + """Return the built-in endpoints, managed host included. + + An unavailable built-in row names what is missing so the owner can repair + it in place: ``unavailable_reason`` is ``executable_not_found`` with the + probed ``executable``, or ``api_key_not_set`` with the ``environment_variable``. + Availability itself stays a live probe; nothing is installed or granted. + """ return [ { "agent_id": "codex", "display_name": "Codex", "adapter_kind": "codex_app_server", - "available": bool(shutil.which(codex_bin)), + **_executable_readiness(codex_bin), "streaming": True, "resume": True, "interrupt": True, @@ -93,7 +117,7 @@ def builtin_chat_endpoints( "agent_id": "claude-code", "display_name": "Claude Code", "adapter_kind": "claude_code_cli", - "available": bool(shutil.which(claude_bin)), + **_executable_readiness(claude_bin), "streaming": True, "resume": True, "interrupt": True, @@ -111,7 +135,7 @@ def builtin_chat_endpoints( "agent_id": KIRO_CLI_CHAT_AGENT_ID, "display_name": KIRO_CLI_CHAT_DISPLAY_NAME, "adapter_kind": KIRO_CLI_CHAT_ADAPTER_KIND, - "available": bool(shutil.which(kiro_cli_bin)), + **_executable_readiness(kiro_cli_bin), "streaming": True, "resume": True, "interrupt": True, @@ -126,7 +150,7 @@ def builtin_chat_endpoints( "agent_id": "anthropic-api", "display_name": "Claude API", "adapter_kind": "anthropic_messages_api", - "available": bool(os.environ.get("ANTHROPIC_API_KEY", "").strip()), + **_environment_readiness("ANTHROPIC_API_KEY"), "streaming": False, "resume": True, "interrupt": False, @@ -138,7 +162,7 @@ def builtin_chat_endpoints( "agent_id": "openai-api", "display_name": "OpenAI API", "adapter_kind": "openai_messages_api", - "available": bool(os.environ.get("OPENAI_API_KEY", "").strip()), + **_environment_readiness("OPENAI_API_KEY"), "streaming": False, "resume": True, "interrupt": False, diff --git a/tests/test_chat_endpoint_readiness.py b/tests/test_chat_endpoint_readiness.py new file mode 100644 index 0000000000..5e24d4d50f --- /dev/null +++ b/tests/test_chat_endpoint_readiness.py @@ -0,0 +1,41 @@ +"""Built-in endpoints name what is missing so the owner can repair it in place.""" + +from __future__ import annotations + +import sys +from pathlib import Path + +from loopx.chat_endpoint_catalog import builtin_chat_endpoints + + +def _rows(monkeypatch, tmp_path: Path, **bins: str) -> dict[str, dict]: + monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False) + monkeypatch.setenv("OPENAI_API_KEY", "set") + rows = builtin_chat_endpoints( + codex_bin=bins.get("codex_bin", "definitely-missing-codex"), + claude_bin=bins.get("claude_bin", "definitely-missing-claude"), + kiro_cli_bin=bins.get("kiro_cli_bin", "definitely-missing-kiro"), + runtime_root=tmp_path, + ) + return {row["agent_id"]: row for row in rows} + + +def test_missing_executables_and_keys_name_their_requirement(monkeypatch, tmp_path: Path) -> None: + rows = _rows(monkeypatch, tmp_path) + for agent_id, executable in (("codex", "definitely-missing-codex"), ("claude-code", "definitely-missing-claude")): + assert rows[agent_id]["available"] is False + assert rows[agent_id]["unavailable_reason"] == "executable_not_found" + assert rows[agent_id]["executable"] == executable + assert rows["anthropic-api"]["available"] is False + assert rows["anthropic-api"]["unavailable_reason"] == "api_key_not_set" + assert rows["anthropic-api"]["environment_variable"] == "ANTHROPIC_API_KEY" + + +def test_present_requirements_report_available_without_a_reason(monkeypatch, tmp_path: Path) -> None: + rows = _rows(monkeypatch, tmp_path, codex_bin=sys.executable) + assert rows["codex"]["available"] is True + assert rows["codex"]["executable"] == sys.executable + assert "unavailable_reason" not in rows["codex"] + assert rows["openai-api"]["available"] is True + assert rows["openai-api"]["environment_variable"] == "OPENAI_API_KEY" + assert "unavailable_reason" not in rows["openai-api"] From 6bf1947451d4437684496bd99f60467f1aba5eef Mon Sep 17 00:00:00 2001 From: song Date: Fri, 16 Oct 2026 00:18:19 +0800 Subject: [PATCH 2/6] test(workspace): prove runtime readiness against the real Chat service Add an end-to-end scenario that starts a disposable real `loopx chat` (serve_chat on a temporary runtime, no registry, no credentials) whose Codex, Claude Code and Kiro executables do not exist, routes the packaged page's catalog, status and project reads to it, and asserts the home notice names the real probed path and the unset API variable. The scenario then installs a Codex executable and clicks Check again: the real probe reports it available and the notice withdraws, read back from the service's own catalog. Nothing on this path is synthetic except the browser; the fixture-driven scenario keeps the timing and negative cases. Co-Authored-By: Claude Fable 5.1 Signed-off-by: song --- examples/personal-workspace-browser-smoke.mjs | 2 + .../runtime-readiness-backend.py | 39 +++++++++++ .../runtime-readiness-live.mjs | 70 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 examples/personal-workspace-browser/runtime-readiness-backend.py create mode 100644 examples/personal-workspace-browser/runtime-readiness-live.mjs diff --git a/examples/personal-workspace-browser-smoke.mjs b/examples/personal-workspace-browser-smoke.mjs index 92fe3638fb..50d4cc794c 100644 --- a/examples/personal-workspace-browser-smoke.mjs +++ b/examples/personal-workspace-browser-smoke.mjs @@ -7,6 +7,7 @@ import {goalWorkspaceM2Scenario} from "./personal-workspace-browser/goal-workspa import {goalWorkspaceSelectionScenario} from "./personal-workspace-browser/goal-workspace-selection.mjs"; import {firstGoalHomeScenario} from "./personal-workspace-browser/first-goal-home.mjs"; import {runtimeReadinessScenario} from "./personal-workspace-browser/runtime-readiness.mjs"; +import {runtimeReadinessLiveScenario} from "./personal-workspace-browser/runtime-readiness-live.mjs"; import { replanCadenceScenario } from "./personal-workspace-browser/replan-cadence.mjs"; import {nativeChildActivityScenario} from "./personal-workspace-browser/native-child-activity.mjs"; import {privateStewardScopeScenario} from "./personal-workspace-browser/private-steward-scope.mjs"; @@ -113,6 +114,7 @@ scenarioCatalog.push(goalWorkspaceCreateScenario); scenarioCatalog.push(goalWorkspaceSelectionScenario); scenarioCatalog.push(firstGoalHomeScenario); scenarioCatalog.push(runtimeReadinessScenario); +scenarioCatalog.push(runtimeReadinessLiveScenario); const requestedScenario = process.env.LOOPX_PERSONAL_WORKSPACE_SCENARIO; scenarioCatalog.push(agentModelSelectionScenario); const scenarios = requestedScenario diff --git a/examples/personal-workspace-browser/runtime-readiness-backend.py b/examples/personal-workspace-browser/runtime-readiness-backend.py new file mode 100644 index 0000000000..870082face --- /dev/null +++ b/examples/personal-workspace-browser/runtime-readiness-backend.py @@ -0,0 +1,39 @@ +"""Real LoopX Chat service on a disposable runtime whose CLI runtimes are absent until the scenario installs them. + +No registry, Goal, model or credential exists here; the service probes the +configured executables live, exactly as an installed LoopX does. +""" +import json +import os +import signal +import socket +import sys +from pathlib import Path + +from loopx.chat_server import default_chat_assets_dir, serve_chat + +root = Path(sys.argv[1]).resolve() +bins = root / "bin" +bins.mkdir(parents=True, exist_ok=True) +(root / "project").mkdir(exist_ok=True) +for variable in ("ANTHROPIC_API_KEY", "OPENAI_API_KEY"): + os.environ.pop(variable, None) +os.environ["LOOPX_USAGE_PING"] = "0" +os.environ["DO_NOT_TRACK"] = "1" +with socket.socket() as probe: + probe.bind(("127.0.0.1", 0)) + port = probe.getsockname()[1] +print(json.dumps({"port": port, "bins": str(bins)}), flush=True) +signal.signal(signal.SIGTERM, lambda *_: sys.exit(0)) +serve_chat( + registry_path=root / "project" / ".loopx" / "registry.json", + runtime_root_override=str(root / "runtime"), + scan_roots=[root / "project"], + host="127.0.0.1", + port=port, + codex_bin=str(bins / "codex"), + claude_bin=str(bins / "claude"), + kiro_cli_bin=str(bins / "kiro-cli"), + assets_dir=default_chat_assets_dir(), + open_browser=False, +) diff --git a/examples/personal-workspace-browser/runtime-readiness-live.mjs b/examples/personal-workspace-browser/runtime-readiness-live.mjs new file mode 100644 index 0000000000..d57e05e4e6 --- /dev/null +++ b/examples/personal-workspace-browser/runtime-readiness-live.mjs @@ -0,0 +1,70 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import { chmod, mkdtemp, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { createInterface } from "node:readline"; +import { waitForHttp } from "../dashboard-browser-smoke-support.mjs"; +import { resolveTestPython } from "../../scripts/test-python.mjs"; +import { outputDir, packaged, repoRoot } from "./fixture.mjs"; +import { fetchWorkspaceBackend, openWorkspacePage } from "./scenario-context.mjs"; + +// End to end against the real service: a disposable `loopx chat` whose Codex, +// Claude Code and Kiro executables do not exist. The packaged page reads the +// real catalog, names the real missing paths, and after the scenario installs +// a Codex executable, Check again re-reads the real probe and withdraws the +// notice. Nothing is synthetic on this path except the browser. + +export const runtimeReadinessLiveScenario = { + id: "runtime-readiness-live", + async run({ browser, collectCoverage, url }) { + if (!packaged) return { coverageEntries: [], note: "live runtime readiness runs against the packaged bundle only" }; + const root = await mkdtemp(join(tmpdir(), "loopx-readiness-live-")); + const python = resolveTestPython({ repoRoot }); + const child = spawn(python, ["-u", resolve(repoRoot, "examples/personal-workspace-browser/runtime-readiness-backend.py"), root], { cwd: repoRoot, env: { ...process.env, PYTHONPATH: repoRoot }, stdio: ["ignore", "pipe", "pipe"] }); + const closed = once(child, "close"); + const lines = createInterface({ input: child.stdout }); + let diagnostics = ""; + child.stderr.on("data", (chunk) => { diagnostics = (diagnostics + chunk).slice(-4000); }); + let session; + try { + const ready = await Promise.race([ + once(lines, "line").then(([line]) => JSON.parse(line)), + closed.then(() => { throw new Error(`readiness backend exited: ${diagnostics}`); }), + ]); + const backend = `http://127.0.0.1:${ready.port}`; + await waitForHttp(`${backend}/chat/`); + session = await openWorkspacePage(browser, url, { collectCoverage, viewport: { width: 1440, height: 900 }, beforeGoto: async (_api, page) => { + for (const pattern of ["**/status.json*", "**/api/chat/capabilities", "**/api/chat/projects"]) { + await page.route(pattern, async (route) => { const response = await fetchWorkspaceBackend(route, backend); await route.fulfill({ response }); }); + } + } }); + const { page } = session; + const notice = page.getByTestId("runtime-readiness"); + await notice.waitFor({ state: "visible", timeout: 20_000 }); + const text = await notice.innerText(); + assert.match(text, new RegExp(`Codex:未在 PATH 中找到命令「${ready.bins.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}/codex」`), `The real service names the probed path: ${text}`); + assert.match(text, /Claude API:未设置环境变量 ANTHROPIC_API_KEY/, "The real service names the unset variable"); + await page.screenshot({ path: resolve(outputDir, "runtime-readiness-live.png"), animations: "disabled" }); + + // The owner installs Codex; Check again re-reads the real probe. + await writeFile(join(ready.bins, "codex"), "#!/bin/sh\nexit 0\n"); + await chmod(join(ready.bins, "codex"), 0o755); + await notice.getByRole("button", { name: "重新检查" }).click(); + await notice.waitFor({ state: "hidden", timeout: 20_000 }); + const catalog = await (await fetch(`${backend}/api/chat/capabilities`)).json(); + const codex = catalog.adapters.find((row) => row.agent_id === "codex"); + assert.equal(codex.available, true, "The real catalog now reports Codex available"); + assert.equal(codex.unavailable_reason, undefined); + return { coverageEntries: await session.close(), note: "real loopx chat service, live PATH probe, real re-check" }; + } catch (error) { + if (diagnostics) process.stderr.write(diagnostics); + await session?.close().catch(() => {}); + throw error; + } finally { + lines.close(); + if (child.exitCode === null && child.signalCode === null) { child.kill("SIGTERM"); const kill = setTimeout(() => child.kill("SIGKILL"), 5000); try { await closed; } finally { clearTimeout(kill); } } + } + }, +}; From 540477ae6bafd525553b812394cbaa1f3dfaea24 Mon Sep 17 00:00:00 2001 From: song Date: Fri, 16 Oct 2026 06:29:20 +0800 Subject: [PATCH 3/6] fix(workspace): keep the readiness notice readable on a phone Review measured the packaged 390px page against a real Chat service that reports absolute executable paths: the notice card ended at 374px while its paragraph and list ran to about 469px, because the implicit grid track kept its min-content width and a probed path is one unbreakable token. The owner could not read the missing command names or the end of the repair text. The notice's single track may now shrink (minmax(0, 1fr)), its children and list items have no minimum width and wrap anywhere, and a code token breaks inside the card; the composer line gets the same wrapping. No component, state or probe changes. The real-service scenario now also sets a 390px viewport and asserts that every paragraph, list item, code token and heading ends inside the card and the viewport, since the document's scroll width alone could not show the clipping. Co-Authored-By: Claude Fable 5.1 Signed-off-by: song --- .../personal-workspace/personal-workspace.css | 7 +++++-- .../runtime-readiness-live.mjs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css index 43d4b00bc4..6710616067 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace.css @@ -332,15 +332,18 @@ button.personal-execution-chip:focus-visible { outline: 2px solid #0070f3; outli .personal-system-health-diagnostics { margin-top: 6px; font-size: var(--text-caption); line-height: var(--leading-caption); } .personal-system-health-diagnostics summary { cursor: pointer; } .personal-system-health-diagnostics ul { margin: 4px 0 0 16px; padding: 0; } -.personal-runtime-readiness { display: grid; gap: 10px; max-width: 640px; min-width: 0; padding: 14px 16px; border: 1px solid var(--pw-amber); border-radius: 12px; background: var(--pw-amber-bg); color: var(--pw-text); } +.personal-runtime-readiness { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; max-width: 640px; min-width: 0; padding: 14px 16px; border: 1px solid var(--pw-amber); border-radius: 12px; background: var(--pw-amber-bg); color: var(--pw-text); } .personal-runtime-readiness h2 { margin: 0; font-size: var(--text-body); line-height: var(--leading-body); font-weight: 600; } .personal-runtime-readiness p { margin: 0; color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); } .personal-runtime-readiness ul { margin: 0; padding-left: 18px; font-size: var(--text-caption); line-height: var(--leading-caption); } +/* A probed executable path is one long token; it must wrap inside the card on a phone instead of widening the track. */ +.personal-runtime-readiness > *, .personal-runtime-readiness li { min-width: 0; overflow-wrap: anywhere; } +.personal-runtime-readiness code { overflow-wrap: anywhere; word-break: break-all; } .personal-runtime-readiness-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; } .personal-runtime-readiness-actions button { min-height: 36px; padding: 0 14px; border: 1px solid var(--pw-amber); border-radius: 8px; background: var(--pw-card); color: var(--pw-amber); font-size: var(--text-caption); font-weight: 600; cursor: pointer; } .personal-runtime-readiness-actions button:disabled { opacity: .6; cursor: wait; } .personal-runtime-readiness-actions a { color: var(--pw-blue-ink); font-size: var(--text-caption); line-height: var(--leading-caption); } -.personal-runtime-readiness-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; color: var(--pw-amber); } +.personal-runtime-readiness-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-width: 0; overflow-wrap: anywhere; color: var(--pw-amber); } .personal-runtime-readiness-line button { min-height: 28px; padding: 0 10px; border: 1px solid var(--pw-amber); border-radius: 6px; background: transparent; color: var(--pw-amber); font-size: var(--text-caption); cursor: pointer; } .personal-runtime-readiness-line a { color: var(--pw-blue-ink); } .personal-home-lane { display: flex; flex-direction: column; min-width: 0; min-height: 100%; border: 1px solid var(--pw-line); border-radius: 16px; background: #fff; box-shadow: 0 1px 4px rgb(30 28 20 / 4%); } diff --git a/examples/personal-workspace-browser/runtime-readiness-live.mjs b/examples/personal-workspace-browser/runtime-readiness-live.mjs index d57e05e4e6..cbbd1fae45 100644 --- a/examples/personal-workspace-browser/runtime-readiness-live.mjs +++ b/examples/personal-workspace-browser/runtime-readiness-live.mjs @@ -48,6 +48,22 @@ export const runtimeReadinessLiveScenario = { assert.match(text, /Claude API:未设置环境变量 ANTHROPIC_API_KEY/, "The real service names the unset variable"); await page.screenshot({ path: resolve(outputDir, "runtime-readiness-live.png"), animations: "disabled" }); + // On a phone the real probed paths must wrap inside the card: every + // paragraph, list item and code token ends within the notice and the + // viewport. The document's own scroll width cannot prove this. + await page.setViewportSize({ width: 390, height: 844 }); + await notice.waitFor({ state: "visible" }); + const bounds = await notice.evaluate((node) => { + const card = node.getBoundingClientRect(); + const children = [...node.querySelectorAll("p, li, code, h2")].map((child) => { const box = child.getBoundingClientRect(); return { tag: child.tagName, right: box.right, width: box.width }; }); + return { card: { right: card.right, width: card.width }, children, viewport: window.innerWidth }; + }); + for (const child of bounds.children) { + assert.ok(child.right <= bounds.card.right + 1 && child.right <= bounds.viewport, `${child.tag} ends at ${child.right} beyond the card (${bounds.card.right}) or the viewport (${bounds.viewport})`); + } + await page.screenshot({ path: resolve(outputDir, "runtime-readiness-live-phone.png"), animations: "disabled" }); + await page.setViewportSize({ width: 1440, height: 900 }); + // The owner installs Codex; Check again re-reads the real probe. await writeFile(join(ready.bins, "codex"), "#!/bin/sh\nexit 0\n"); await chmod(join(ready.bins, "codex"), 0o755); From df9d3243361c254e786eab8eaf347907c55ab34e Mon Sep 17 00:00:00 2001 From: song Date: Fri, 16 Oct 2026 14:25:43 +0800 Subject: [PATCH 4/6] fix(workspace): scope execution diagnosis and retain unavailable routes Signed-off-by: song --- apps/presentation/dashboard/package.json | 2 +- .../dashboard/smoke/chat-route-smoke.ts | 16 ++++++++++++---- .../dashboard/src/data/chat-model.ts | 10 +++++++--- apps/presentation/dashboard/src/data/chat.ts | 2 +- .../src/features/personal-workspace/i18n.tsx | 6 ++++-- .../personal-workspace-page.tsx | 3 ++- .../dashboard/src/views/dashboard-page.tsx | 7 ++++--- 7 files changed, 31 insertions(+), 15 deletions(-) diff --git a/apps/presentation/dashboard/package.json b/apps/presentation/dashboard/package.json index 4803bdfd6e..93df623143 100644 --- a/apps/presentation/dashboard/package.json +++ b/apps/presentation/dashboard/package.json @@ -21,7 +21,7 @@ "smoke:benchmark-study": "rm -rf node_modules/.cache/loopx-benchmark-study-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --resolveJsonModule --outDir node_modules/.cache/loopx-benchmark-study-smoke smoke/benchmark-study-contract-smoke.ts src/data/benchmark-study.ts && node node_modules/.cache/loopx-benchmark-study-smoke/smoke/benchmark-study-contract-smoke.js", "smoke:benchmark-study-browser": "node ../../../examples/dashboard-benchmark-study-browser-smoke.mjs", "smoke:capability-configuration": "rm -rf node_modules/.cache/loopx-capability-configuration-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --types node --skipLibCheck --strict --outDir node_modules/.cache/loopx-capability-configuration-smoke smoke/capability-configuration-smoke.ts src/data/capability-configuration.ts && node node_modules/.cache/loopx-capability-configuration-smoke/smoke/capability-configuration-smoke.js", - "smoke:chat-route": "tsc --ignoreConfig --target ES2022 --module ES2022 --moduleResolution Bundler --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir node_modules/.cache/loopx-chat-route-smoke smoke/chat-route-smoke.ts src/data/chat-model.ts src/vite-env.d.ts && node node_modules/.cache/loopx-chat-route-smoke/smoke/chat-route-smoke.js", + "smoke:chat-route": "tsc --ignoreConfig --target ES2022 --module ES2022 --moduleResolution Bundler --ignoreDeprecations 6.0 --skipLibCheck --strict --rootDir ../../.. --outDir node_modules/.cache/loopx-chat-route-smoke smoke/chat-route-smoke.ts src/data/chat-model.ts src/vite-env.d.ts && node node_modules/.cache/loopx-chat-route-smoke/apps/presentation/dashboard/smoke/chat-route-smoke.js", "smoke:chat-stream-stall": "vite build --ssr smoke/chat-stream-stall-smoke.ts --outDir node_modules/.cache/loopx-chat-stream-stall --emptyOutDir && node node_modules/.cache/loopx-chat-stream-stall/chat-stream-stall-smoke.js", "smoke:chat-turn-acceptance-retry": "vite build --ssr smoke/chat-turn-acceptance-retry-smoke.ts --outDir node_modules/.cache/loopx-chat-turn-acceptance-retry --emptyOutDir && node node_modules/.cache/loopx-chat-turn-acceptance-retry/chat-turn-acceptance-retry-smoke.js", "smoke:demo-readiness": "bash ../../../scripts/loopx-python.sh --exec ../../../examples/dashboard-demo-readiness-smoke.py", diff --git a/apps/presentation/dashboard/smoke/chat-route-smoke.ts b/apps/presentation/dashboard/smoke/chat-route-smoke.ts index a9503c31c4..e30b7f46ca 100644 --- a/apps/presentation/dashboard/smoke/chat-route-smoke.ts +++ b/apps/presentation/dashboard/smoke/chat-route-smoke.ts @@ -46,7 +46,7 @@ type ChatRouteCandidate = { }; const capabilityAwareRoute = chatModel as unknown as { - selectAvailableChatAgent?: ( + selectChatAgent?: ( options: ChatRouteCandidate[], preferredAgentId: string | undefined, defaultAgentId: string, @@ -54,7 +54,7 @@ const capabilityAwareRoute = chatModel as unknown as { }; check( - typeof capabilityAwareRoute.selectAvailableChatAgent === "function", + typeof capabilityAwareRoute.selectChatAgent === "function", "Chat routing should expose stale-selection fallback", ); @@ -64,17 +64,25 @@ const routeOptions: ChatRouteCandidate[] = [ { agentId: "status-only", available: true }, ]; check( - capabilityAwareRoute.selectAvailableChatAgent!(routeOptions, "registered-fixer", "codex").agentId === "codex", + capabilityAwareRoute.selectChatAgent!(routeOptions, "registered-fixer", "codex").agentId === "codex", "a persisted unavailable Agent should fall back to Codex", ); check( - capabilityAwareRoute.selectAvailableChatAgent!( + capabilityAwareRoute.selectChatAgent!( routeOptions.map((option) => option.agentId === "codex" ? { ...option, available: false } : option), "registered-fixer", "status-only", ).agentId === "status-only", "Chat should fall back to status-only when no model adapter is available", ); +const unavailableProjectOptions = routeOptions.filter(option => option.agentId !== "status-only") + .map(option => ({ ...option, available: false })); +const diagnosticRoute = capabilityAwareRoute.selectChatAgent!(unavailableProjectOptions, "registered-fixer", "codex"); +check(diagnosticRoute.agentId === "registered-fixer" && diagnosticRoute.available === false, + "An unavailable project keeps the preferred identity and its refusal, rather than crashing or granting execution"); +const missingPreferred = capabilityAwareRoute.selectChatAgent!(unavailableProjectOptions, "status-only", "status-only"); +check(missingPreferred.agentId === "codex" && missingPreferred.available === false, + "A project without status projection keeps an actual unavailable executor, never a fabricated readonly route"); const fixture: ChatStatus = { ok: true, diff --git a/apps/presentation/dashboard/src/data/chat-model.ts b/apps/presentation/dashboard/src/data/chat-model.ts index 8807628af7..0bafe53d7a 100644 --- a/apps/presentation/dashboard/src/data/chat-model.ts +++ b/apps/presentation/dashboard/src/data/chat-model.ts @@ -109,7 +109,8 @@ export type ChatRouteCandidate = { available: boolean; }; -export function selectAvailableChatAgent( +/** Prefer an executable route; keep an unavailable candidate for diagnosis when none can run. */ +export function selectChatAgent( options: T[], preferredAgentId: string | undefined, defaultAgentId: string, @@ -118,9 +119,12 @@ export function selectAvailableChatAgent( option.agentId === preferredAgentId && option.available ) ?? options.find((option) => option.agentId === defaultAgentId && option.available - ) ?? options.find((option) => option.available); + ) ?? options.find((option) => option.available) + ?? options.find((option) => option.agentId === preferredAgentId) + ?? options.find((option) => option.agentId === defaultAgentId) + ?? options[0]; if (!selected) { - throw new Error("Chat requires at least one available route"); + throw new Error("Chat requires at least one route"); } return selected; } diff --git a/apps/presentation/dashboard/src/data/chat.ts b/apps/presentation/dashboard/src/data/chat.ts index 6309f3c00a..161c8c5991 100644 --- a/apps/presentation/dashboard/src/data/chat.ts +++ b/apps/presentation/dashboard/src/data/chat.ts @@ -56,7 +56,7 @@ export { pendingGoalReviews, proposalReviewState, sessionInvalidatedByPayload, - selectAvailableChatAgent, + selectChatAgent, selectChatGoal, stewardPrompts, turnReplaySafeByPayload, diff --git a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx index d547eabb09..e562f0dd4b 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx @@ -598,7 +598,8 @@ const en = { "readiness.missingOther": "{label}: not available ({reason})", "readiness.missingUnknown": "{label}: not available", "readiness.selectedUnavailable": "{label} is not available on this machine. Install and sign in, then check again, or pick another runtime.", - "readiness.selectedUnavailableNone": "No Agent runtime is ready on this machine, so messages cannot be sent yet.", + "readiness.selectedUnavailableNone": "Agent execution is not ready on this machine. Set up an execution runtime first.", + "readiness.selectedUnavailableReadonly": "Agent execution is not ready. Status only can still answer questions about existing work.", "readiness.recheck": "Check again", "readiness.rechecking": "Checking…", "readiness.guide": "Setup guide", @@ -2145,7 +2146,8 @@ const zhCN: Record = { "readiness.missingOther": "{label}:不可用({reason})", "readiness.missingUnknown": "{label}:不可用", "readiness.selectedUnavailable": "{label} 在这台机器上不可用。安装并登录后重新检查,或选择其他运行环境。", - "readiness.selectedUnavailableNone": "这台机器上还没有可用的 Agent 运行环境,暂时无法发送消息。", + "readiness.selectedUnavailableNone": "Agent 执行尚未就绪,请先完成执行运行环境设置。", + "readiness.selectedUnavailableReadonly": "Agent 执行尚未就绪;仍可使用「仅查状态」查询已有工作。", "readiness.recheck": "重新检查", "readiness.rechecking": "正在检查…", "readiness.guide": "安装指引", diff --git a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx index d1eddbf50f..02f0922f53 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx @@ -2768,7 +2768,8 @@ export function PersonalWorkspacePage({ {imageAttachmentError ?

{imageAttachmentError}

: null} {!readOnly && (selectedGoal || managerChatOpen) && (runtimeReadiness.kind === "none_available" || runtimeReadiness.kind === "selected_unavailable") ? (

- {runtimeReadiness.kind === "none_available" ? t("readiness.selectedUnavailableNone") : t("readiness.selectedUnavailable", { label: runtimeReadiness.selected.label })} + {runtimeReadiness.kind === "none_available" ? t(agents.some((agent) => agent.adapterKind === "status_projection" && agent.available) + ? "readiness.selectedUnavailableReadonly" : "readiness.selectedUnavailableNone") : t("readiness.selectedUnavailable", { label: runtimeReadiness.selected.label })} {callbacks.onRecheckRuntimes ? : null} {t("readiness.guide")}

diff --git a/apps/presentation/dashboard/src/views/dashboard-page.tsx b/apps/presentation/dashboard/src/views/dashboard-page.tsx index 08c2c0b34d..d4eac6c128 100644 --- a/apps/presentation/dashboard/src/views/dashboard-page.tsx +++ b/apps/presentation/dashboard/src/views/dashboard-page.tsx @@ -67,7 +67,7 @@ import { sendChatTurnStreaming, chatSessionQueuesFollowUps, chatSessionSupportsSteering, - selectAvailableChatAgent, + selectChatAgent, sessionInvalidatedByPayload, isTodoProposal, type ChatSessionSnapshot, @@ -1406,7 +1406,7 @@ function PersonalGoalHome({ const [selectedAgents, setSelectedAgents] = useState>(readPersonalAgentSelections); const [selectedAgentModels, setSelectedAgentModels] = useState>({}); const selectedAgentId = selectedAgents[contextId] ?? agentDefaultForContext(contextId); - const selectedAgent = selectAvailableChatAgent(agentOptions, selectedAgentId, defaultAgentId); + const selectedAgent = selectChatAgent(agentOptions, selectedAgentId, defaultAgentId); const [messagesByContext, setMessagesByContext] = useState>({}); const [runtimeBindings, setRuntimeBindings] = useState>({}); // Bound Sessions whose mode queues a message sent while a Turn runs, read @@ -2175,8 +2175,9 @@ function PersonalGoalHome({ ? model.goals.find((goal) => goal.goalId === targetContext.goalId) ?? null : null; const selectedRoute = route?.agentId - ? selectAvailableChatAgent(agentOptions, route.agentId, defaultAgentId) + ? selectChatAgent(agentOptions, route.agentId, defaultAgentId) : selectedAgent; + if (!selectedRoute.available) throw new Error(t("agent.workspaceUnavailable")); const targetQuestionModel = targetContextId === "manager" ? model : targetGoal From 7d50d074a7a6b281b98101cb7281dfd2597c0405 Mon Sep 17 00:00:00 2001 From: song Date: Fri, 16 Oct 2026 14:25:43 +0800 Subject: [PATCH 5/6] test(workspace): qualify readonly sends and unavailable project recovery Signed-off-by: song --- .../runtime-readiness-backend.py | 47 ++++++---- .../runtime-readiness-live.mjs | 91 ++++++++++++++++++- .../runtime-readiness.mjs | 5 +- 3 files changed, 120 insertions(+), 23 deletions(-) diff --git a/examples/personal-workspace-browser/runtime-readiness-backend.py b/examples/personal-workspace-browser/runtime-readiness-backend.py index 870082face..38bfdcde4f 100644 --- a/examples/personal-workspace-browser/runtime-readiness-backend.py +++ b/examples/personal-workspace-browser/runtime-readiness-backend.py @@ -1,18 +1,27 @@ """Real LoopX Chat service on a disposable runtime whose CLI runtimes are absent until the scenario installs them. -No registry, Goal, model or credential exists here; the service probes the -configured executables live, exactly as an installed LoopX does. +The optional Goal is a disposable fixture; no model or credential exists here. +The service probes only the explicitly configured temporary executables. """ import json import os +import runpy import signal import socket import sys +import tempfile from pathlib import Path -from loopx.chat_server import default_chat_assets_dir, serve_chat - root = Path(sys.argv[1]).resolve() +home = root / "home" +home.mkdir(parents=True, exist_ok=True) +os.environ["HOME"] = str(home) +tempfile.tempdir = str(root) +for variable in ("TMPDIR", "TEMP", "TMP"): + os.environ[variable] = str(root) +from loopx.chat_server import default_chat_assets_dir, serve_chat # noqa: E402 +from loopx.control_plane.effect_runtime import restart_effect_runtime # noqa: E402 + bins = root / "bin" bins.mkdir(parents=True, exist_ok=True) (root / "project").mkdir(exist_ok=True) @@ -20,20 +29,26 @@ os.environ.pop(variable, None) os.environ["LOOPX_USAGE_PING"] = "0" os.environ["DO_NOT_TRACK"] = "1" +if "--with-goal" in sys.argv[2:]: + fixture = runpy.run_path(str(Path(__file__).resolve().parents[1] / "loopx-chat-actions-smoke.py")) + fixture["write_registry_fixture"](root) with socket.socket() as probe: probe.bind(("127.0.0.1", 0)) port = probe.getsockname()[1] print(json.dumps({"port": port, "bins": str(bins)}), flush=True) signal.signal(signal.SIGTERM, lambda *_: sys.exit(0)) -serve_chat( - registry_path=root / "project" / ".loopx" / "registry.json", - runtime_root_override=str(root / "runtime"), - scan_roots=[root / "project"], - host="127.0.0.1", - port=port, - codex_bin=str(bins / "codex"), - claude_bin=str(bins / "claude"), - kiro_cli_bin=str(bins / "kiro-cli"), - assets_dir=default_chat_assets_dir(), - open_browser=False, -) +try: + serve_chat( + registry_path=root / "project" / ".loopx" / "registry.json", + runtime_root_override=str(root / "runtime"), + scan_roots=[root / "project"], + host="127.0.0.1", + port=port, + codex_bin=str(bins / "codex"), + claude_bin=str(bins / "claude"), + kiro_cli_bin=str(bins / "kiro-cli"), + assets_dir=default_chat_assets_dir(), + open_browser=False, + ) +finally: + restart_effect_runtime() diff --git a/examples/personal-workspace-browser/runtime-readiness-live.mjs b/examples/personal-workspace-browser/runtime-readiness-live.mjs index cbbd1fae45..0470ee8fc5 100644 --- a/examples/personal-workspace-browser/runtime-readiness-live.mjs +++ b/examples/personal-workspace-browser/runtime-readiness-live.mjs @@ -14,7 +14,8 @@ import { fetchWorkspaceBackend, openWorkspacePage } from "./scenario-context.mjs // Claude Code and Kiro executables do not exist. The packaged page reads the // real catalog, names the real missing paths, and after the scenario installs // a Codex executable, Check again re-reads the real probe and withdraws the -// notice. Nothing is synthetic on this path except the browser. +// notice. The Goal and exit-only executable are synthetic; catalog, messages, +// persistence and HTTP are the real local owners. No model is called. export const runtimeReadinessLiveScenario = { id: "runtime-readiness-live", @@ -22,7 +23,7 @@ export const runtimeReadinessLiveScenario = { if (!packaged) return { coverageEntries: [], note: "live runtime readiness runs against the packaged bundle only" }; const root = await mkdtemp(join(tmpdir(), "loopx-readiness-live-")); const python = resolveTestPython({ repoRoot }); - const child = spawn(python, ["-u", resolve(repoRoot, "examples/personal-workspace-browser/runtime-readiness-backend.py"), root], { cwd: repoRoot, env: { ...process.env, PYTHONPATH: repoRoot }, stdio: ["ignore", "pipe", "pipe"] }); + const child = spawn(python, ["-u", resolve(repoRoot, "examples/personal-workspace-browser/runtime-readiness-backend.py"), root, "--with-goal"], { cwd: repoRoot, env: { ...process.env, PYTHONPATH: repoRoot }, stdio: ["ignore", "pipe", "pipe"] }); const closed = once(child, "close"); const lines = createInterface({ input: child.stdout }); let diagnostics = ""; @@ -36,9 +37,7 @@ export const runtimeReadinessLiveScenario = { const backend = `http://127.0.0.1:${ready.port}`; await waitForHttp(`${backend}/chat/`); session = await openWorkspacePage(browser, url, { collectCoverage, viewport: { width: 1440, height: 900 }, beforeGoto: async (_api, page) => { - for (const pattern of ["**/status.json*", "**/api/chat/capabilities", "**/api/chat/projects"]) { - await page.route(pattern, async (route) => { const response = await fetchWorkspaceBackend(route, backend); await route.fulfill({ response }); }); - } + await page.route((target) => target.pathname === "/status.json" || target.pathname.startsWith("/api/chat/") || target.pathname.startsWith("/api/actions") || target.pathname.startsWith("/extension-"), async (route) => { const response = await fetchWorkspaceBackend(route, backend); await route.fulfill({ response }); }); } }); const { page } = session; const notice = page.getByTestId("runtime-readiness"); @@ -64,6 +63,86 @@ export const runtimeReadinessLiveScenario = { await page.screenshot({ path: resolve(outputDir, "runtime-readiness-live-phone.png"), animations: "disabled" }); await page.setViewportSize({ width: 1440, height: 900 }); + const turns = []; + const admissions = []; + page.on("request", (request) => { + if (request.method() !== "POST") return; + const path = new URL(request.url()).pathname; + if (/\/turns$/.test(path)) turns.push(request.url()); + if (path === "/api/chat/sessions") admissions.push(request.url()); + }); + const receipts = []; + const projectRefusals = []; + for (const locale of ["zh-CN", "en"]) { + const zh = locale === "zh-CN"; + await page.evaluate((value) => localStorage.setItem("loopx-pw-locale", value), locale); + await page.reload({ waitUntil: "networkidle" }); + await page.setViewportSize({ width: 1440, height: 900 }); + await page.locator(".personal-manager-link").click(); + await page.getByRole("navigation", { name: zh ? "管家视图" : "Manager view" }).getByRole("button", { name: /^(Chat|对话)$/ }).click(); + for (const scope of ["manager", "goal"]) { + if (scope === "goal") { + await page.locator(".personal-goal-link").first().click(); + await page.getByRole("navigation", { name: zh ? "Goal 视图" : "Goal view" }).getByRole("button", { name: zh ? "对话" : "Chat", exact: true }).click(); + } + await page.getByRole("combobox", { name: zh ? "选择聊天 Runtime" : "Select chat runtime", exact: true }).click(); + await page.getByRole("option", { name: zh ? "仅查状态" : "Status only", exact: true }).click(); + const question = zh ? `当前有哪些 Goal?${scope}` : `Which Goals are present? ${scope}`; + const saved = page.waitForResponse((response) => new URL(response.url()).pathname === "/api/chat/projection-messages" && response.request().method() === "POST"); + await page.getByLabel(zh ? "向 LoopX 发送消息" : "Send a message to LoopX").fill(question); + await page.getByRole("button", { name: zh ? "发送" : "Send", exact: true }).click(); + const response = await saved; + assert.equal(response.status(), 201, "Status-only still persists the accepted question and answer"); + const receipt = await response.json(); + const stored = await (await fetch(`${backend}/api/chat/sessions/${receipt.session_id}`)).json(); + assert.equal(stored.session.adapter_kind, "status_projection"); + assert.ok(stored.messages.some((message) => message.message_id === receipt.user_message_id && message.text === question)); + const answer = stored.messages.find((message) => message.message_id === receipt.answer_message_id); + assert.ok(answer?.text); + await page.getByText(question, { exact: true }).waitFor({ state: "visible" }); + await page.setViewportSize({ width: 390, height: 844 }); + const line = page.getByTestId("runtime-readiness-line"); + const copy = await line.innerText(); + assert.doesNotMatch(copy, /无法发送消息|messages cannot be sent/, "Execution diagnosis must not deny the successful read-only task"); + assert.match(copy, zh ? /Agent 执行.*仅查状态/ : /Agent execution.*Status only/); + await page.screenshot({ path: resolve(outputDir, `runtime-readiness-${scope}-${locale}-phone.png`), animations: "disabled" }); + receipts.push({ scope, locale, session_id: receipt.session_id, user_message_id: receipt.user_message_id, answer_message_id: receipt.answer_message_id, status: response.status() }); + await page.setViewportSize({ width: 1440, height: 900 }); + } + // A project conversation has no status-only executor. Its diagnosis + // must not promise a readonly entry that this context does not offer. + await page.locator(".personal-manager-link").click(); + await page.getByRole("navigation", { name: zh ? "管家视图" : "Manager view" }).getByRole("button", { name: /^(Chat|对话)$/ }).click(); + const projects = await (await fetch(`${backend}/api/chat/projects`)).json(); + assert.ok(projects.projects.length > 0); + await page.getByRole("combobox", { name: zh ? "范围" : "Scope", exact: true }).click(); + await page.getByRole("option", { name: projects.projects[0].title, exact: true }).click(); + const projectLine = page.getByTestId("runtime-readiness-line"); + await projectLine.waitFor({ state: "visible" }); + assert.match(await projectLine.innerText(), zh ? /Agent 执行尚未就绪/ : /Agent execution is not ready/); + assert.doesNotMatch(await projectLine.innerText(), /仅查状态|Status only/); + await page.getByRole("combobox", { name: zh ? "选择聊天 Runtime" : "Select chat runtime", exact: true }).click(); + assert.equal(await page.getByRole("option", { name: zh ? "仅查状态" : "Status only", exact: true }).count(), 0); + await page.keyboard.press("Escape"); + const admissionsBefore = admissions.length; + const draft = zh ? "保留这条未发送的项目请求" : "Retain this unsent project request"; + const input = page.getByLabel(zh ? "向 LoopX 发送消息" : "Send a message to LoopX"); + await input.fill(draft); + await page.getByRole("button", { name: zh ? "发送" : "Send", exact: true }).click(); + await page.locator(".personal-action-feedback").getByText(zh ? /发送失败.*需先修复运行环境/ : /Send failed.*repair the runtime first/).waitFor(); + assert.equal(await input.inputValue(), draft, "A refused execution keeps the owner's draft"); + assert.equal(admissions.length, admissionsBefore, "An unavailable route cannot request a Session"); + projectRefusals.push({ locale, draft_retained: true, session_admission_delta: 0 }); + } + assert.equal(turns.length, 0, "Status-only never starts a model Turn"); + await writeFile(resolve(outputDir, "runtime-readiness-status-only-receipts.json"), JSON.stringify({ real_local_service: true, receipts, project_refusals: projectRefusals, model_turns: turns.length }, null, 2)); + + // Return to the same home notice; a repaired execution CLI still clears it. + await page.evaluate(() => localStorage.setItem("loopx-pw-locale", "zh-CN")); + await page.reload({ waitUntil: "networkidle" }); + await page.locator(".personal-manager-link").click(); + await page.getByRole("navigation", { name: "管家视图" }).getByRole("button", { name: "总览", exact: true }).click(); + // The owner installs Codex; Check again re-reads the real probe. await writeFile(join(ready.bins, "codex"), "#!/bin/sh\nexit 0\n"); await chmod(join(ready.bins, "codex"), 0o755); @@ -75,6 +154,8 @@ export const runtimeReadinessLiveScenario = { assert.equal(codex.unavailable_reason, undefined); return { coverageEntries: await session.close(), note: "real loopx chat service, live PATH probe, real re-check" }; } catch (error) { + await session?.page.screenshot({ path: resolve(outputDir, "runtime-readiness-live-failure.png"), animations: "disabled" }).catch(() => {}); + if (session?.errors.length) process.stderr.write(`${JSON.stringify(session.errors)}\n`); if (diagnostics) process.stderr.write(diagnostics); await session?.close().catch(() => {}); throw error; diff --git a/examples/personal-workspace-browser/runtime-readiness.mjs b/examples/personal-workspace-browser/runtime-readiness.mjs index 17e7f7254c..8daf179455 100644 --- a/examples/personal-workspace-browser/runtime-readiness.mjs +++ b/examples/personal-workspace-browser/runtime-readiness.mjs @@ -37,7 +37,7 @@ export const runtimeReadinessScenario = { await notice.waitFor({ state: "hidden", timeout: 15_000 }); assert.equal(await page.getByTestId("runtime-readiness").count(), 0, "A repaired runtime clears the notice"); - // With a Goal open and every runtime missing, the composer says why nothing can be sent. + // With a Goal open and execution runtimes missing, status-only remains usable. api.runtimeAgentsOverride = missing; await page.getByRole("button", { name: "刷新状态", exact: true }).click(); await page.locator(".personal-goal-row").first().locator(".personal-goal-link").click(); @@ -45,7 +45,8 @@ export const runtimeReadinessScenario = { await page.getByRole("navigation", { name: "Goal 视图" }).getByRole("button", { name: "对话", exact: true }).click(); const line = page.getByTestId("runtime-readiness-line"); await line.waitFor({ state: "visible", timeout: 15_000 }); - assert.match(await line.innerText(), /还没有可用的 Agent 运行环境|不可用/); + assert.match(await line.innerText(), /Agent 执行尚未就绪.*仅查状态/); + assert.doesNotMatch(await line.innerText(), /无法发送消息/); await page.screenshot({ path: resolve(outputDir, "runtime-readiness-composer.png"), animations: "disabled" }); api.runtimeAgentsOverride = repaired; await line.getByRole("button", { name: "重新检查" }).click(); From ac7e97bc3bd53f3e7dfe554f59e25716645a7aad Mon Sep 17 00:00:00 2001 From: song Date: Fri, 16 Oct 2026 14:25:43 +0800 Subject: [PATCH 6/6] docs(workspace): distinguish execution readiness from readonly chat Signed-off-by: song --- docs/guides/personal-workspace-user-guide.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/guides/personal-workspace-user-guide.md b/docs/guides/personal-workspace-user-guide.md index e5c1169b9f..988bf306e0 100644 --- a/docs/guides/personal-workspace-user-guide.md +++ b/docs/guides/personal-workspace-user-guide.md @@ -53,6 +53,8 @@ LoopX 通过本机已安装的命令行工具驱动 Agent(Codex、Claude Code 未设置的环境变量),并提供「重新检查」和「安装指引」。安装并登录后点「重新检查」,页面重新读取 服务的运行环境目录并回读结果;本页不会安装任何东西,也不授予任何权限。打开 Goal 对话时,如果选中的 运行环境在本机不可用,输入框上方会给出同样的说明和重新检查。 +执行环境缺失不影响管家和 Goal 中的「仅查状态」:它保存原问题与状态回答,不启动 Agent。 +没有该选项的项目对话会明确拒绝未就绪的执行路线,并保留未发送的草稿。 LoopX drives Agents through a command-line tool installed on this machine (Codex, Claude Code, Kiro CLI) or through an API key. When none is ready, the manager overview says so at the top, @@ -61,6 +63,9 @@ offers **Check again** and a **Setup guide** link. After installing and signing re-reads the service's runtime catalog and reads the result back; the page installs nothing and grants nothing. In a Goal conversation, the same notice and re-check appear above the composer when the selected runtime is unavailable here. +Missing execution runtimes do not disable **Status only** in manager and Goal conversations: it +stores the original question and status answer without starting an Agent. A project conversation +without that option refuses an unavailable execution route and retains the unsent draft. ## 🧭 2. 控制台核心架构