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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import assert from "node:assert/strict";

import { changeStewardExecutorDraft, goalReplanCadenceEditorValue, goalReplanCadenceWriteValue, hasReplanCadenceOverride, goalStorageEditorValue, parseEditableCapabilityJson, projectEditableCapabilityConfiguration, replanCadenceEditorDefaults } from "../src/data/capability-configuration.js";
import { changeStewardExecutorDraft, goalExploreEditorValue, goalExploreWriteValue, goalReplanCadenceEditorValue, goalReplanCadenceWriteValue, hasReplanCadenceOverride, goalStorageEditorValue, parseEditableCapabilityJson, projectEditableCapabilityConfiguration, replanCadenceEditorDefaults } from "../src/data/capability-configuration.js";

const exploreEditor = {fields: [{key: "mode"}, {key: "profile"}, {key: "composition_mode"}, {key: "composition_scope_id", nullable: true}]};
const exploreEffective = {mode: "planning", profile: "generic", composition_mode: "disabled", composition_scope_id: null};
for (const current of [{profile: "adaptive-resilient"}, {composition_mode: "explicit_only", composition_scope_id: "bounded-scope"}]) {
const draft = projectEditableCapabilityConfiguration(exploreEditor, goalExploreEditorValue(current, exploreEffective));
assert.equal(draft.mode, "planning", "sparse Goal overrides display the live inherited mode");
assert.equal(Object.hasOwn(goalExploreWriteValue({...draft, profile: "generic"}, false), "mode"), false,
"saving another field must not pin the inherited mode");
assert.equal(goalExploreWriteValue({...draft, mode: "off"}, true).mode, "off", "an explicit off choice remains writable");
const json = parseEditableCapabilityJson(exploreEditor, JSON.stringify(goalExploreWriteValue(draft, false)))!;
assert.equal(Object.hasOwn(json, "mode"), false, "form to JSON preserves inheritance intent");
assert.equal(projectEditableCapabilityConfiguration(exploreEditor, json, exploreEffective).mode, "planning",
"sparse JSON returning to the form displays the effective mode");
}
assert.equal(goalExploreEditorValue({mode: "evidence"}, exploreEffective).mode, "evidence",
"stored explicit modes win even when device preferences change");

const codexDraft = { executor_endpoint: "codex", executor_model: "gpt-fixture", executor_reasoning_effort: "high", selection_policy: "preferred" };
const managedDraft = changeStewardExecutorDraft(codexDraft, "executor_endpoint", "dsh");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ export function goalReplanCadenceWriteValue(
return value;
}

/** Sparse Goal settings display live inherited fields without pinning them. */
export function goalExploreEditorValue(current: unknown, effective: unknown): Record<string, unknown> {
return {...configurationObject(effective), ...configurationObject(current)};
}

/** Only a stored or deliberately edited mode becomes a Goal override. */
export function goalExploreWriteValue(draft: Record<string, unknown>, modeOverride: boolean): Record<string, unknown> {
const value = {...draft};
if (!modeOverride) delete value.mode;
return value;
}

/** A v0 storage selector did not enable creation; opening Settings must not. */
export function goalStorageEditorValue(value: unknown): unknown {
const record = configurationObject(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type GoalConfigurationPreview,
type GoalConfigurationPartialWrite,
} from "../../data/chat";
import { goalReplanCadenceEditorValue, goalReplanCadenceWriteValue, hasReplanCadenceOverride, parseEditableCapabilityJson, projectEditableCapabilityConfiguration, replanCadenceEditorDefaults } from "../../data/capability-configuration";
import { goalExploreEditorValue, goalExploreWriteValue, goalReplanCadenceEditorValue, goalReplanCadenceWriteValue, hasReplanCadenceOverride, parseEditableCapabilityJson, projectEditableCapabilityConfiguration, replanCadenceEditorDefaults } from "../../data/capability-configuration";
import { useWorkspaceI18n } from "./i18n";
import { CapabilityConfigurationFields } from "./capability-configuration-fields";
import { withReportScheduleTimezone } from "./periodic-report-schedule-field";
Expand Down Expand Up @@ -39,16 +39,18 @@ type CapabilityMutationState = Readonly<{
draft: Record<string, unknown>;
cadenceOverride: boolean;
horizonOverride: boolean;
exploreModeOverride: boolean;
partialWrite: GoalConfigurationPartialWrite | null;
preview: GoalConfigurationPreview | null;
}>;

function goalWriteDraft(selected: CapabilityConfigurationCatalog["capabilities"][number], draft: Record<string, unknown>, editorMode: "guided" | "json", cadenceOverride: boolean, horizonOverride: boolean) {
function goalWriteDraft(selected: CapabilityConfigurationCatalog["capabilities"][number], draft: Record<string, unknown>, editorMode: "guided" | "json", cadenceOverride: boolean, horizonOverride: boolean, exploreModeOverride: boolean) {
const defaults = selected.capability_id === "todo_replan_cadence"
? editorMode === "json" ? undefined : replanCadenceEditorDefaults(selected.effective_configuration?.configuration, selected.default)
: selected.default;
const writable = projectEditableCapabilityConfiguration(selected.configuration_editor, draft, defaults);
if (selected.capability_id === "reward_memory") return rewardMemoryWriteValue(writable);
if (selected.capability_id === "explore_harness") return goalExploreWriteValue(writable, exploreModeOverride);
if (selected.capability_id === "todo_replan_cadence" && editorMode === "guided") {
return goalReplanCadenceWriteValue(writable, cadenceOverride, horizonOverride);
}
Expand Down Expand Up @@ -79,6 +81,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
draft: {},
cadenceOverride: false,
horizonOverride: false,
exploreModeOverride: false,
partialWrite: null,
preview: null,
});
Expand All @@ -105,13 +108,16 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
selected?.configuration_editor ?? { fields: [] },
selected?.capability_id === "todo_replan_cadence"
? goalReplanCadenceEditorValue(selected.current, selected.effective_configuration?.configuration)
: selected?.capability_id === "explore_harness"
? goalExploreEditorValue(selected.current, selected.effective_configuration?.configuration)
: selected?.capability_id === "reward_memory"
? rewardMemoryScopeDraft(selected.current, memoryScope.current.goalId === goalId ? memoryScope.current.agentId : "")
: current,
selected?.default,
),
cadenceOverride: hasReplanCadenceOverride(selected?.current),
horizonOverride: Object.hasOwn(selected?.current ?? {}, "planning_horizon_goal"),
exploreModeOverride: Object.hasOwn(selected?.current ?? {}, "mode"),
partialWrite: null,
preview: null,
});
Expand All @@ -123,7 +129,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
setMutation((current) => ({ ...current, busy: "preview", partialWrite: null }));
setError(null);
try {
const nextPreview = await previewGoalConfiguration(goalId, selected.capability_id, configuration === null ? null : goalWriteDraft(selected, configuration, editorMode, mutation.cadenceOverride, mutation.horizonOverride));
const nextPreview = await previewGoalConfiguration(goalId, selected.capability_id, configuration === null ? null : goalWriteDraft(selected, configuration, editorMode, mutation.cadenceOverride, mutation.horizonOverride, mutation.exploreModeOverride));
setMutation((current) => ({ ...current, preview: nextPreview }));
} catch (reason) {
setError(reason instanceof Error ? reason.message : t("capabilities.previewFailed"));
Expand All @@ -137,7 +143,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
setMutation((current) => ({ ...current, busy: "apply" }));
setError(null);
try {
const writableDraft = goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride, mutation.horizonOverride);
const writableDraft = goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride, mutation.horizonOverride, mutation.exploreModeOverride);
const result = await applyGoalConfiguration(
goalId,
selected.capability_id,
Expand Down Expand Up @@ -172,6 +178,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
: { ...current.draft, [key]: value },
cadenceOverride: current.cadenceOverride || (selected?.capability_id === "todo_replan_cadence" && (key === "count_unit" || key === "count")),
horizonOverride: current.horizonOverride || (selected?.capability_id === "todo_replan_cadence" && key === "planning_horizon_goal"),
exploreModeOverride: current.exploreModeOverride || (selected?.capability_id === "explore_harness" && key === "mode"),
preview: null,
}));
setError(null);
Expand All @@ -186,27 +193,33 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read
}
setMutation((current) => ({ ...current, preview: null, draft: parsed
? projectEditableCapabilityConfiguration(selected.configuration_editor, parsed,
selected.capability_id === "todo_replan_cadence" ? undefined : selected.default)
selected.capability_id === "todo_replan_cadence" ? undefined
: selected.capability_id === "explore_harness" ? selected.effective_configuration?.configuration : selected.default)
: current.draft,
cadenceOverride: selected.capability_id === "todo_replan_cadence" && parsed
? hasReplanCadenceOverride(parsed) : current.cadenceOverride,
horizonOverride: selected.capability_id === "todo_replan_cadence" && parsed
? Object.hasOwn(parsed, "planning_horizon_goal") : current.horizonOverride,
exploreModeOverride: selected.capability_id === "explore_harness" && parsed
? Object.hasOwn(parsed, "mode") : current.exploreModeOverride,
}));
setError(null);
}

function changeMode() {
if (loading || mutation.busy || !jsonValid) return;
if (editorMode === "guided" && selected) {
const jsonValue = selected.capability_id === "todo_replan_cadence"
? goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride, mutation.horizonOverride)
const jsonValue = ["todo_replan_cadence", "explore_harness"].includes(selected.capability_id)
? goalWriteDraft(selected, mutation.draft, editorMode, mutation.cadenceOverride, mutation.horizonOverride, mutation.exploreModeOverride)
: mutation.draft;
setJsonDraft(JSON.stringify(jsonValue, null, 2));
}
setEditorMode(editorMode === "guided" ? "json" : "guided");
setMutation((current) => ({ ...current, preview: null,
draft: editorMode === "json" && selected?.capability_id === "todo_replan_cadence"
draft: editorMode === "json" && selected?.capability_id === "explore_harness"
? projectEditableCapabilityConfiguration(selected.configuration_editor,
goalExploreEditorValue(current.draft, selected.effective_configuration?.configuration), selected.default)
: editorMode === "json" && selected?.capability_id === "todo_replan_cadence"
? projectEditableCapabilityConfiguration(selected.configuration_editor, current.draft,
replanCadenceEditorDefaults(selected.effective_configuration?.configuration, selected.default))
: current.draft,
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/rfcs/desktop-execution-frontends-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ editor or storage path happens to exist.
| `todo_replan_cadence` | Device default + Goal override | Reuse review frequency while allowing a Goal's work rhythm. This is completed-work review cadence, not the automation timer. |
| `multi_subagent` | Goal only | Child model, capacity, responsibility domains and delegation bindings apply to one Goal's team and resource boundary. |
| `peer_task_coordination` | Goal only | Coordinator identity must refer to a member of this Goal, not an inherited unrelated Agent. |
| `explore_harness`, `explore_graph` | Goal only | Exploration strategy and opt-in affect this Goal's planning and evidence graph. |
| `explore_harness` | Device mode default + Goal override | Existing and new Goals inherit off/evidence/planning live. Explicit legacy enabled flags or Goal mode override it; profiles, composition scopes and external delivery authority stay Goal-local. |
| `progress_review` | Goal only | Drift mode and threshold belong with this Goal's pinned acceptance contract revision. |
| `reward_memory` | Goal only | Private provider binding and enabled Agents are Goal-local; a global preference cannot spread that data binding. |
| `lark_kanban_heartbeat_sync` | Goal only | The target board and synchronization follow this Goal's binding and audience. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ manager Agent,也不把连接器硬编码到 Codex。
| `todo_replan_cadence` | 设备默认值 + Goal 覆盖 | 复用复盘频率并允许 Goal 工作节奏;这是已完成工作复盘频率,不是 automation timer。 |
| `multi_subagent` | 仅 Goal | 子模型、容量、职责域和委托 binding 属于一个 Goal 的团队与资源边界。 |
| `peer_task_coordination` | 仅 Goal | coordinator 必须是本 Goal 成员,不能继承无关 Agent 身份。 |
| `explore_harness`、`explore_graph` | 仅 Goal | 探索策略与 opt-in 只影响本 Goal 的规划和证据图。 |
| `explore_harness` | 设备模式默认值 + Goal 覆盖 | 已有与新建 Goal 实时继承 off/evidence/planning。Goal 的显式模式或旧 enabled 标志优先;规划 profile、组合范围和外部交付权限仍属于各 Goal。 |
| `progress_review` | 仅 Goal | drift 模式与阈值绑定本 Goal 固定的验收合同版本。 |
| `reward_memory` | 仅 Goal | 私有 provider binding 和启用 Agent 是 Goal 局部配置,设备偏好不能扩散数据 binding。 |
| `lark_kanban_heartbeat_sync` | 仅 Goal | 目标看板与同步遵循本 Goal 的 binding 和受众。 |
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 35 additions & 2 deletions loopx/capabilities/configuration_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ def capability_configuration_editor(
"fields": [_field("enabled", "Enabled", "boolean")],
},
"explore_harness": {
"supported_scopes": ["goal"],
"writable_scopes": ["goal"],
"supported_scopes": ["machine", "goal"],
"writable_scopes": ["machine", "goal"],
Comment thread
loopx-agent marked this conversation as resolved.
"fields": [
_field("mode", "Exploration mode", "select", options=("off", "evidence", "planning"),
description="Evidence only, or evidence with planning. Spawn authority is separate."),
Expand Down Expand Up @@ -573,6 +573,11 @@ def _machine_catalog_entry(
explore_harness_profiles=explore_harness_profiles,
),
}
if capability_id == "explore_harness":
# Machine preference owns only the mode; profiles and research scope
# stay on each Goal. Reuse the existing mode field and generic editor.
editor = entry["configuration_editor"]
editor["fields"] = [field for field in editor["fields"] if field["key"] == "mode"]
if isinstance(namespace.get("current"), Mapping):
entry["machine_current"] = deepcopy(namespace["current"])
if isinstance(namespace.get("configuration_template"), Mapping):
Expand Down Expand Up @@ -610,10 +615,13 @@ def _merge_goal_feature(
for field in ("default", "current", "documentation", "context_contribution"):
if isinstance(feature.get(field), Mapping):
entry[field] = deepcopy(feature[field])
previous_editor = entry.get("configuration_editor")
entry["configuration_editor"] = capability_configuration_editor(
capability_id,
explore_harness_profiles=feature.get("profiles") or explore_harness_profiles,
)
if capability_id == "explore_harness" and "default" not in feature and previous_editor:
entry["configuration_editor"] = previous_editor
if capability_id == "reward_memory":
for field in entry["configuration_editor"]["fields"]:
if field["key"] == "agent_id":
Expand Down Expand Up @@ -669,6 +677,31 @@ def _attach_effective_configuration(entry: dict[str, Any]) -> None:
"planning_horizon_goal": DEFAULT_PLANNING_HORIZON_GOAL, **goal_override}
machine_default = None if machine_default is None else {
"planning_horizon_goal": DEFAULT_PLANNING_HORIZON_GOAL, **machine_default}
if entry["capability_id"] == "explore_harness":
from .explore.machine_defaults import resolve_explore_configuration
from ..explore_graph import plan_explore_configuration

raw = dict(goal_override or {})
graph = {}
if "mode" in raw:
planned = plan_explore_configuration({}, {"mode": raw.pop("mode")})
graph["enabled"] = planned["evidence_enabled"]
raw["enabled"] = planned["planning_enabled"]
resolved = resolve_explore_configuration(
{"explore_graph": graph, "spawn_policy": {"explore_harness": raw}},
machine_default.get("mode") if machine_default is not None else None,
)
value = resolved["configuration"]
source = resolved["mode_source"]
entry["effective_configuration"] = {
"schema_version": CAPABILITY_CONFIGURATION_RESOLUTION_SCHEMA,
"capability_id": "explore_harness", "configuration": value,
"source": source, "inherited": source == "machine_default",
"goal_override_present": goal_override is not None,
"machine_default_present": machine_default is not None,
"effective_revision": configuration_payload_revision({"source": source, "configuration": value}),
}
return
entry["effective_configuration"] = resolve_capability_configuration(
str(entry["capability_id"]),
goal_override=goal_override,
Expand Down
Loading
Loading