Remove plan mode, skill toolcall path, and inline netx tools.

Wave C subtraction: keep skills as prompt-only, route netx via MCP with a shared netx_http client for xlsx/context inject, and delete plan_agent_v2 from the gateway.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-11 01:17:39 +08:00
parent 628a9dffd2
commit efa72df362
40 changed files with 169 additions and 3033 deletions

View file

@ -33,7 +33,6 @@ from svc.files.session_export import export_session_json, export_session_markdow
from svc.persistence.sqlite_store import SqliteStore
from svc.persistence.assistant_store import get_assistant_store
from runtime.gateway import OclawGateway
from runtime.plan_agent_v2.switch import v2_feature_enabled
from runtime.types import StandardMessage, normalize_interaction_mode, normalize_requested_specialist
from runtime.chat.history_tool_result_compact import compact_tool_results_in_session_history
from runtime.chat.persist_terminal_fallback import persist_assistant_text_if_turn_missing
@ -1280,7 +1279,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor
"execution_mode": s_em,
"confirm_strategy": u_cs,
"plan_agent_version": u_pav,
"plan_agent_v2_globally_enabled": bool(v2_feature_enabled(store=store)),
"plan_agent_v2_globally_enabled": False,
"global_menu": {
"interaction_mode": u_im,
"specialist": u_sp,
@ -1325,7 +1324,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor
"execution_mode": s_em,
"confirm_strategy": u_cs,
"plan_agent_version": u_pav,
"plan_agent_v2_globally_enabled": bool(v2_feature_enabled(store=store)),
"plan_agent_v2_globally_enabled": False,
"global_menu": {
"interaction_mode": u_im,
"specialist": u_sp,
@ -1349,7 +1348,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor
"specialist": u_sp,
"confirm_strategy": u_cs,
"plan_agent_version": u_pav,
"plan_agent_v2_globally_enabled": bool(v2_feature_enabled(store=store)),
"plan_agent_v2_globally_enabled": False,
}
@chat.post("/user-mode")
@ -1377,15 +1376,15 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor
plan_agent_version=plan_agent_version,
)
u_im, u_sp, u_cs, u_pav = _resolve_user_menu_chat_settings(store=store, tenant_id=tenant_id, user_id=user_id)
# Mirror Plan/Agent version to the gateway feature gate (⋯ menu is the control surface).
store.set_setting("AIA_EXPERT_PLAN_AGENT_V2_ENABLED", "1" if str(u_pav or "").strip().lower() == "v2" else "0")
# Plan mode removed; keep setting key cleared for leftover clients.
store.set_setting("AIA_EXPERT_PLAN_AGENT_V2_ENABLED", "0")
return {
"ok": True,
"interaction_mode": u_im,
"specialist": u_sp,
"confirm_strategy": u_cs,
"plan_agent_version": u_pav,
"plan_agent_v2_globally_enabled": bool(v2_feature_enabled(store=store)),
"plan_agent_v2_globally_enabled": False,
}
@chat.get("/admin/user-stats")

View file

@ -98,14 +98,11 @@ def include_skill_routes(
ctx = resolve_auth(store, authorization)
_require_admin(ctx)
raw_prompt = str(store.get_setting("AIA_SKILLS_PROMPT_IN_SYSTEM") or "").strip().lower()
raw_toolcall = str(store.get_setting("AIA_SKILL_TOOLCALL_ENABLED") or "").strip().lower()
prompt_in_system = raw_prompt not in {"0", "false", "no", "off"}
toolcall_enabled = raw_toolcall in {"1", "true", "yes", "on"}
market_provider = normalize_skill_market_provider_setting(str(store.get_setting(_SKILL_MARKET_PROVIDER_KEY) or ""))
return {
"ok": True,
"prompt_in_system": bool(prompt_in_system),
"toolcall_enabled": bool(toolcall_enabled),
"market_provider": market_provider,
}
@ -120,14 +117,10 @@ def include_skill_routes(
_require_admin(ctx)
if "prompt_in_system" in payload:
store.set_setting("AIA_SKILLS_PROMPT_IN_SYSTEM", "1" if bool(payload.get("prompt_in_system")) else "0")
if "toolcall_enabled" in payload:
store.set_setting("AIA_SKILL_TOOLCALL_ENABLED", "1" if bool(payload.get("toolcall_enabled")) else "0")
if "market_provider" in payload:
store.set_setting(_SKILL_MARKET_PROVIDER_KEY, normalize_skill_market_provider_setting(str(payload.get("market_provider") or "")))
raw_prompt = str(store.get_setting("AIA_SKILLS_PROMPT_IN_SYSTEM") or "").strip().lower()
raw_toolcall = str(store.get_setting("AIA_SKILL_TOOLCALL_ENABLED") or "").strip().lower()
prompt_in_system = raw_prompt not in {"0", "false", "no", "off"}
toolcall_enabled = raw_toolcall in {"1", "true", "yes", "on"}
market_provider = normalize_skill_market_provider_setting(str(store.get_setting(_SKILL_MARKET_PROVIDER_KEY) or ""))
_audit(
store,
@ -137,14 +130,12 @@ def include_skill_routes(
status="ok",
detail={
"prompt_in_system": bool(prompt_in_system),
"toolcall_enabled": bool(toolcall_enabled),
"market_provider": market_provider,
},
)
return {
"ok": True,
"prompt_in_system": bool(prompt_in_system),
"toolcall_enabled": bool(toolcall_enabled),
"market_provider": market_provider,
}
@ -817,7 +808,7 @@ def include_skill_routes(
raise HTTPException(status_code=500, detail="tool_registry_unavailable")
spec = tools.get(name)
if spec is None:
# Skill prompt mode: toolcall may be disabled, fallback to direct runtime execution from manifest.
# Skill prompt mode: execute runtime entry from manifest when invoked via Admin/skills APIs.
mf = next((m for m in discover_workspace_skill_manifests() if str(m.name or "").strip() == name), None)
if mf is None:
raise HTTPException(status_code=404, detail="tool_not_found")

View file

@ -8235,7 +8235,6 @@ async function renderSkills() {
const marketStatus = el("div", { class: "muted", text: "" });
const skillModeStatus = el("div", { class: "muted", text: "" });
const skillPromptModeCb = el("input", { type: "checkbox" });
const skillToolcallModeCb = el("input", { type: "checkbox" });
const skillMarketProviderSelect = el("select", { class: "input", style: "min-width:160px;" }, [
el("option", { value: "clawhub", text: "clawhub (ClawHub)" }),
el("option", { value: "cocoloop", text: "cocoloop (CocoLoop)" }),
@ -8250,7 +8249,6 @@ async function renderSkills() {
try {
const r = await apiGet("/admin/api/skills/mode");
skillPromptModeCb.checked = !!r.prompt_in_system;
skillToolcallModeCb.checked = !!r.toolcall_enabled;
const mp = String(r.market_provider || "clawhub").trim().toLowerCase();
skillMarketProviderSelect.value = mp === "cocoloop" ? "cocoloop" : "clawhub";
skillModeStatus.textContent = "";
@ -8263,14 +8261,12 @@ async function renderSkills() {
try {
const r = await apiPost("/admin/api/skills/mode", {
prompt_in_system: !!skillPromptModeCb.checked,
toolcall_enabled: !!skillToolcallModeCb.checked,
market_provider: String(skillMarketProviderSelect.value || "clawhub").trim(),
});
skillPromptModeCb.checked = !!r.prompt_in_system;
skillToolcallModeCb.checked = !!r.toolcall_enabled;
const mp = String(r.market_provider || "clawhub").trim().toLowerCase();
skillMarketProviderSelect.value = mp === "cocoloop" ? "cocoloop" : "clawhub";
skillModeStatus.textContent = `saved: prompt=${String(!!r.prompt_in_system)} toolcall=${String(!!r.toolcall_enabled)} market=${String(skillMarketProviderSelect.value)}`;
skillModeStatus.textContent = `saved: prompt=${String(!!r.prompt_in_system)} market=${String(skillMarketProviderSelect.value)}`;
} catch (e) {
skillModeStatus.textContent = `mode: ${String(e && e.message ? e.message : e)}`;
}
@ -9610,7 +9606,6 @@ async function renderSkills() {
el("div", { class: "card__title", text: t("title.skills") }),
el("div", { class: "row", style: "gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:8px;" }, [
el("label", { class: "row", style: "gap:6px;align-items:center;" }, [skillPromptModeCb, el("span", { text: "Prompt mode (inject SKILL.md)" })]),
el("label", { class: "row", style: "gap:6px;align-items:center;" }, [skillToolcallModeCb, el("span", { text: "Toolcall mode (runtime as tools)" })]),
el("label", { class: "row", style: "gap:6px;align-items:center;flex-wrap:wrap;" }, [
el("span", { text: "Market (AIA_SKILL_MARKET_PROVIDER)" }),
skillMarketProviderSelect,

View file

@ -3430,13 +3430,8 @@ async function renderChatUi() {
return normalizePlanAgentVersion(currentPlanAgentVersion);
};
const refreshExecUi = () => {
const modeVal = String(globalMenuModeValue || MAIN_MODE_VALUE).toLowerCase();
const expert = modeVal !== MAIN_MODE_VALUE;
const show =
expert &&
planAgentV2GloballyEnabled &&
normalizePlanAgentVersion(currentPlanAgentVersion) === PLAN_AGENT_V2;
execSelectWrap.style.display = show ? "inline-flex" : "none";
// Plan mode removed; keep agent execution only.
execSelectWrap.style.display = "none";
};
const modeOptionLabel = (v) => {
const key = String(v || "").trim().toLowerCase();