Skip to content

Commit 6598103

Browse files
committed
Merge remote-tracking branch 'origin/main' into codex/coalesce-live-packet-rendering
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
2 parents 562ca62 + be7789f commit 6598103

210 files changed

Lines changed: 12641 additions & 1291 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/presentation/dashboard/package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@
2929
"smoke:goal-order": "tsc --ignoreConfig --target ES2022 --module ES2022 --moduleResolution Bundler --skipLibCheck --strict --outDir node_modules/.cache/loopx-goal-order src/features/personal-workspace/goal-order.ts && node src/features/personal-workspace/goal-order.test.mjs",
3030
"smoke:home-browser": "node ../../../examples/dashboard-home-browser-smoke.mjs",
3131
"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",
32-
"smoke:personal-workspace": "npm run smoke:goal-order && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs",
33-
"smoke:personal-workspace-packaged": "npm run smoke:goal-order && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs",
32+
"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",
33+
"smoke:personal-workspace": "npm run smoke:goal-order && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && node ../../../examples/personal-workspace-browser-smoke.mjs",
34+
"smoke:personal-workspace-packaged": "npm run smoke:goal-order && npm run smoke:delegation-preflight && node src/features/personal-workspace/workspace-theme.test.mjs && node src/features/personal-workspace/personal-workspace-contract.test.mjs && LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 LOOPX_PLAYWRIGHT_PACKAGE=\"$PWD/node_modules/playwright\" node ../../../examples/personal-workspace-browser-smoke.mjs",
3435
"smoke:personal-workspace-router": "rm -rf /tmp/loopx-personal-workspace-router-smoke && tsc --ignoreConfig --target ES2022 --module commonjs --moduleResolution node --ignoreDeprecations 6.0 --skipLibCheck --strict --outDir /tmp/loopx-personal-workspace-router-smoke smoke/personal-workspace-router-smoke.ts src/features/personal-workspace/personal-workspace-router.ts && node /tmp/loopx-personal-workspace-router-smoke/smoke/personal-workspace-router-smoke.js",
3536
"smoke:presentation-surface-schema": "rm -rf /tmp/loopx-presentation-surface-schema-smoke && tsc --ignoreConfig --target ES2022 --module CommonJS --moduleResolution Node --ignoreDeprecations 6.0 --skipLibCheck --strict --resolveJsonModule --esModuleInterop --outDir /tmp/loopx-presentation-surface-schema-smoke smoke/presentation-surface-schema-smoke.ts src/data/status.ts src/data/decision-research.ts src/data/goal-channel-frontstage.ts && NODE_PATH=\"$PWD/node_modules\" node /tmp/loopx-presentation-surface-schema-smoke/apps/presentation/dashboard/smoke/presentation-surface-schema-smoke.js",
3637
"smoke:projection-localization": "rm -rf /tmp/loopx-projection-localization-smoke && tsc --ignoreConfig --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck --strict --outDir /tmp/loopx-projection-localization-smoke smoke/projection-localization-smoke.ts src/features/personal-workspace/projection-localization.ts && node /tmp/loopx-projection-localization-smoke/smoke/projection-localization-smoke.js",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {renderToStaticMarkup} from "react-dom/server";
2+
import type {DelegationPreflight} from "../src/data/delegation-preflight.js";
3+
import {DelegationPreflightStatus} from "../src/features/personal-workspace/delegation-preflight-status.js";
4+
5+
const unavailable: DelegationPreflight = {
6+
state: "authority_unavailable",
7+
turn_eligible: false,
8+
acceptance_ready: false,
9+
turn_route: null,
10+
authority_ready: false,
11+
authority_reason: "Goal acceptance requires an existing canonical authority",
12+
executor: null,
13+
effects: {
14+
host_invoked: false,
15+
state_written: false,
16+
quota_spent: false,
17+
scheduler_acknowledged: false,
18+
},
19+
};
20+
21+
for (const [zh, expectedLabel, expectedBoundary] of [
22+
[true, "缺少规范权限状态", "未检查或启动执行器"],
23+
[false, "Canonical authority unavailable", "No executor was inspected or launched"],
24+
] as const) {
25+
const html = renderToStaticMarkup(<DelegationPreflightStatus check={unavailable} zh={zh}/>);
26+
if (!html.includes(expectedLabel)) throw new Error(`missing state label: ${expectedLabel}`);
27+
if (!html.includes("Goal acceptance requires an existing canonical authority")) throw new Error("missing authority reason");
28+
if (!html.includes(expectedBoundary)) throw new Error(`missing boundary copy: ${expectedBoundary}`);
29+
if (/Local launch prerequisites met|本机启动条件已满足/.test(html)) throw new Error("rendered launchable copy for unavailable authority");
30+
}
31+
if (Object.values(unavailable.effects).some(Boolean)) throw new Error("authority-unavailable response must report zero effects");
32+
33+
console.log("delegation authority-unavailable preflight smoke passed");

‎apps/presentation/dashboard/src/data/chat.ts‎

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
type TodoApplyResult,
99
type TodoPreview,
1010
} from "./chat-model.js";
11+
import type {DelegationPreflight} from "./delegation-preflight.js";
1112

1213
const configuredChatOrigin = String(import.meta.env?.VITE_LOOPX_CHAT_ORIGIN ?? "")
1314
.trim()
@@ -113,6 +114,11 @@ export const managerChannelBindingSchema = z.object({
113114
executor_kind: z.string(),
114115
model: z.string(),
115116
model_source: z.string(),
117+
selection_policy: z.enum(["preferred", "pinned", "flexible"]).default("preferred"),
118+
allocation_reason: z.string().default(""),
119+
configured_endpoint: z.string().nullable().optional(),
120+
eligible_endpoints: z.array(z.string()).default([]),
121+
allocation_configuration_revision: z.string().default(""),
116122
credential_env_var: z.string(),
117123
operator_credential_configured: z.boolean(),
118124
output_token_budget: z.object({
@@ -895,11 +901,39 @@ export type DelegationInventory = {
895901
artifacts?: Array<{ref: string; sha256: string}>}>;
896902
has_more: boolean; next_cursor: string | null; page_readback_complete: boolean;
897903
};
898-
export type DelegationPreflight = {
899-
state: "turn_blocked" | "acceptance_unavailable" | "runtime_unavailable" | "runtime_unverified" | "launchable";
900-
turn_eligible: boolean; acceptance_ready: boolean; turn_route: string;
901-
executor: {host: string; available: boolean | null; reason: string | null; profile: string | null};
904+
export type {DelegationPreflight} from "./delegation-preflight.js";
905+
export type DelegationDependency = {
906+
operation_id: string; ref: string; sha256: string; input_ref: string;
907+
relation: "responds_to" | "revises" | "uses"; state: "current" | "unavailable";
902908
};
909+
export type DelegationAdoption = {
910+
requester_agent_id: string; consumer_operation_id: string; consumer_request_id: string;
911+
consumer_agent_id: string; consumer_todo_id: string; state: "current" | "unavailable";
912+
source_artifacts: Array<{ref: string; sha256: string}>;
913+
consumer_artifacts: Array<{ref: string; sha256: string}>;
914+
};
915+
export type DelegationReadback = {
916+
operation_id: string; request_id: string; agent_id: string; todo_id: string;
917+
status: string; worker_active: boolean; recovery_required: boolean;
918+
artifacts?: Array<{ref: string; sha256: string; text: string}>; error?: string;
919+
dependencies?: DelegationDependency[]; adoptions?: DelegationAdoption[];
920+
};
921+
export function readLoopXTeamWork(sessionId: string, operationId: string) {
922+
return requestJson<DelegationReadback>(`/api/chat/sessions/${sessionId}/loopx`, {
923+
method: "POST", body: JSON.stringify({operation: "read", operation_id: operationId}),
924+
});
925+
}
926+
// Keep inventory and selected-operation labels consistent; unknown states stay unknown.
927+
export function delegationStateLabel(row: {status: string; worker_active?: boolean; recovery_required: boolean | null}, zh: boolean) {
928+
if (row.status === "unavailable") return zh ? "无法核验" : "Unavailable";
929+
if (row.status === "accepted") return zh ? "已通过当前验收" : "Currently accepted";
930+
if (row.status === "rejected") return zh ? "未通过验收" : "Rejected";
931+
if (row.recovery_required) return zh ? "需要恢复原执行" : "Original execution needs recovery";
932+
if (row.status === "running" && row.worker_active) return zh ? "执行中" : "Executing";
933+
if (row.status === "turn_returned" && row.worker_active) return zh ? "正在验收" : "Validating";
934+
if (["prepared", "running", "turn_returned"].includes(row.status)) return zh ? "已派发,等待执行回读" : "Dispatched; awaiting execution readback";
935+
return zh ? "状态未知" : "Unknown state";
936+
}
903937
export function fetchLoopXTeamWork(sessionId: string, cursor?: string) {
904938
return requestJson<DelegationInventory>(`/api/chat/sessions/${sessionId}/loopx`, {
905939
method: "POST", body: JSON.stringify({operation: "operations", limit: 10, ...(cursor ? {cursor} : {})}),
@@ -915,9 +949,9 @@ export function updateLoopXMode(sessionId: string, operation: string, settings?:
915949
method: "POST", body: JSON.stringify({operation, operation_id: operationId, ...(settings ? {settings} : {})}),
916950
});
917951
}
918-
export function sendLoopXMessage(sessionId: string, message: string, deliveryMode: "queue" | "inbox" | "steer") {
952+
export function sendLoopXMessage(sessionId: string, message: string, deliveryMode: "queue" | "inbox" | "steer", operationId: string = crypto.randomUUID()) {
919953
return requestJson<{ok: true; status: string; delivery_mode: string}>(`/api/chat/sessions/${sessionId}/loopx`, {
920-
method: "POST", body: JSON.stringify({operation: "message", operation_id: crypto.randomUUID(), message, delivery_mode: deliveryMode}),
954+
method: "POST", body: JSON.stringify({operation: "message", operation_id: operationId, message, delivery_mode: deliveryMode}),
921955
});
922956
}
923957

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export type DelegationPreflight = {
2+
state: "authority_unavailable" | "turn_blocked" | "acceptance_unavailable" | "runtime_unavailable" | "runtime_unverified" | "launchable";
3+
turn_eligible: boolean;
4+
acceptance_ready: boolean;
5+
turn_route: string | null;
6+
authority_ready: boolean;
7+
authority_reason: string | null;
8+
executor: {host: string; available: boolean | null; reason: string | null; profile: string | null} | null;
9+
effects: {host_invoked: boolean; state_written: boolean; quota_spent: boolean; scheduler_acknowledged: boolean};
10+
};

‎apps/presentation/dashboard/src/features/personal-workspace/capability-localization.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
1818
},
1919
steward_executor: {
2020
displayName: "Steward executor",
21-
description: "Selects the executor, model, and reasoning effort the steward channel answers on for this machine, ahead of the Chat service environment.",
21+
description: "Guides the steward executor, model, and selection boundary for this machine. A pinned route blocks substitution; a flexible pool permits only authorized fallback.",
2222
},
2323
todo_replan_cadence: { displayName: "Goal review cadence", description: "Configures the Goal review cadence." },
2424
change_quality_qualification: {
@@ -74,7 +74,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
7474
},
7575
steward_executor: {
7676
displayName: "管家执行器",
77-
description: "选择本机管家通道使用的执行器、模型与推理档位,优先级高于服务环境变量。",
77+
description: "配置本机管家的执行器、模型与选择边界;锁定路径禁止替代,灵活池只允许在已授权范围内回退。",
7878
},
7979
todo_replan_cadence: { displayName: "Goal 复核周期", description: "配置 Goal 的复核周期。" },
8080
change_quality_qualification: {
@@ -128,7 +128,9 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
128128
const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
129129
en: {
130130
runtime_profile: { label: "Runtime profile", description: "Restricted keeps scoped LoopX reads only. Trusted owner enables normal host tools while protected operations retain separate checks." },
131-
executor_endpoint: { label: "Steward executor", description: "The executor this machine's steward channel answers on. The choice outranks the Chat service environment and the shipped default." },
131+
selection_policy: { label: "Selection policy", description: "Preferred allows an explicit user choice; pinned rejects another executor; flexible permits fallback only inside the eligible pool." },
132+
executor_endpoint: { label: "Primary steward executor", description: "The preferred or pinned executor for this machine. In a flexible pool it is tried first when available." },
133+
eligible_endpoints: { label: "Flexible eligible executors", description: "One authorized executor per line. Use only with flexible selection and include the primary executor." },
132134
executor_model: { label: "Model", description: "Optional model for the selected executor. Leave blank to keep the executor's own default." },
133135
executor_reasoning_effort: { label: "Reasoning effort", description: "Optional reasoning effort for the selected executor. Leave blank to keep the executor's own default." },
134136
completed_todos: { label: "Completed Todos between Goal reviews", description: "Machine default or explicit Goal override, from 1 to 5." },
@@ -152,7 +154,9 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
152154
},
153155
"zh-CN": {
154156
runtime_profile: { label: "运行模式", description: "restricted 仅使用受限 LoopX 读取;trusted_owner 开放常规宿主工具,但受保护操作仍单独校验。" },
155-
executor_endpoint: { label: "管家执行器", description: "本机管家通道使用的执行器;优先级高于 Chat 服务环境变量与出货默认值。" },
157+
selection_policy: { label: "选择策略", description: "preferred 允许用户显式改选;pinned 拒绝其他执行器;flexible 只在已授权资源池内回退。" },
158+
executor_endpoint: { label: "首选管家执行器", description: "本机首选或锁定的执行器;灵活池模式下优先尝试它。" },
159+
eligible_endpoints: { label: "灵活池可用执行器", description: "每行一个已授权执行器,仅用于 flexible;必须包含首选执行器。" },
156160
executor_model: { label: "模型", description: "所选执行器使用的模型,可留空;留空表示沿用执行器自身的默认模型。" },
157161
executor_reasoning_effort: { label: "推理档位", description: "所选执行器使用的推理档位,可留空;留空表示沿用执行器自身的默认档位。" },
158162
completed_todos: { label: "两次 Goal 复核间的已完成 Todo 数", description: "可设置 1–5;机器默认值可被 Goal 显式覆盖。" },

‎apps/presentation/dashboard/src/features/personal-workspace/channel-header.tsx‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,30 @@ export function ChannelHeader({
8787
&& managerChannelBinding.executor_endpoint_default_reason === "steward_channel_default"
8888
? "header.managerEndpointStewardDefault"
8989
: null;
90+
const managerSelectionPolicyLabel = managerChannelBinding
91+
? t(managerChannelBinding.selection_policy === "pinned"
92+
? "header.managerSelectionPinned"
93+
: managerChannelBinding.selection_policy === "flexible"
94+
? "header.managerSelectionFlexible"
95+
: "header.managerSelectionPreferred")
96+
: null;
97+
const managerAllocationReasonLabel = managerChannelBinding?.allocation_reason
98+
? t(managerChannelBinding.allocation_reason === "user_explicit"
99+
? "header.managerAllocationUser"
100+
: managerChannelBinding.allocation_reason === "pinned_configuration"
101+
? "header.managerAllocationPinned"
102+
: managerChannelBinding.allocation_reason === "flexible_availability_fallback"
103+
? "header.managerAllocationFallback"
104+
: managerChannelBinding.allocation_reason === "flexible_pool_unavailable"
105+
? "header.managerAllocationUnavailable"
106+
: managerChannelBinding.allocation_reason === "flexible_primary_available"
107+
? "header.managerAllocationPrimary"
108+
: managerChannelBinding.allocation_reason === "product_default"
109+
? "header.managerAllocationProductDefault"
110+
: managerChannelBinding.allocation_reason === "service_override"
111+
? "header.managerAllocationService"
112+
: "header.managerAllocationConfigured")
113+
: null;
90114

91115
const runtimeControl = readOnlySourceLabel ? (
92116
<span className="personal-read-only-source" title={t("header.readOnlySourceDescription", { source: readOnlySourceLabel })}><Eye size={15} />{readOnlySourceLabel}<small>{t("common.readOnly")}</small></span>
@@ -142,6 +166,12 @@ export function ChannelHeader({
142166
sandbox: managerRuntime.sandbox,
143167
})}</p>
144168
) : null}
169+
{managerSelectionPolicyLabel && managerAllocationReasonLabel && managerChannelBinding ? (
170+
<p>{t("header.managerAllocation", {
171+
policy: managerSelectionPolicyLabel,
172+
reason: managerAllocationReasonLabel,
173+
})}</p>
174+
) : null}
145175
{managerExecutionDefaultReason && managerChannelBinding ? (
146176
<span className="personal-execution-rule-note">
147177
{t(managerExecutionDefaultReason, { executor: managerChannelBinding.executor_endpoint })}

‎apps/presentation/dashboard/src/features/personal-workspace/channel-timeline.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function ChannelTimeline({
7777
return (
7878
<button className={`personal-proposal-row is-${item.proposal.status}`} key={item.id} onClick={() => onSelect({ item: item.proposal, kind: "proposal" })} type="button">
7979
<span><Sparkles size={17} /></span>
80-
<span><small>{item.proposal.actionKind} · {item.proposal.status}</small><strong>{item.proposal.title}</strong><p>{item.proposal.impact}</p></span>
80+
<span><small>{item.proposal.actionKind} · {item.proposal.status}</small><strong>{item.proposal.title}</strong>{item.proposal.impact ? <p>{item.proposal.impact}</p> : null}</span>
8181
<b>{item.proposal.status === "gated" && item.proposal.actionKind !== "operation.execute" ? t("timeline.review") : item.proposal.primaryLabel ?? t("timeline.reviewAndConfirm")}</b>
8282
</button>
8383
);

‎apps/presentation/dashboard/src/features/personal-workspace/context-drawer.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
10101010
{selection.item.actionKind === "team.plan" && selection.item.status === "applied" ? <TeamPlanResult proposal={selection.item} t={t} /> : <section className="personal-proposal-card">
10111011
<small>{selection.item.actionKind} · {selection.item.status}</small>
10121012
<h3>{selection.item.title}</h3>
1013-
<p>{selection.item.impact}</p>
1013+
{selection.item.impact ? <p>{selection.item.impact}</p> : null}
10141014
{selection.item.reviewPlan && !selection.item.reviewPlan.retryOriginal && selection.item.actionKind !== "team.plan" ? <p className="personal-proposal-explainer" data-action-review={selection.item.reviewPlan.interaction}>{selection.item.actionKind === "operation.execute" && selection.item.status === "gated"
10151015
? t("actionReview.operation_group_confirmation")
10161016
: selection.item.actionKind === "operation.execute" && selection.item.reviewPlan.reason === "readback_unverified"

0 commit comments

Comments
 (0)