Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
08a6edd
✨ Feature: Introduce jiuwen as an optional agent core.
YehongPan Oct 12, 2026
61201e4
Merge remote-tracking branch 'origin/develop' into pyh/feat_jiuwen_ag…
YehongPan Oct 12, 2026
2a1edf7
fix(agent-core): configure native MCP HTTP client explicitly
YehongPan Oct 12, 2026
b7ea864
fix(agent-core): isolate legacy Jiuwen import workarounds
YehongPan Oct 12, 2026
951e64f
Merge remote-tracking branch 'origin/develop' into pyh/feat_jiuwen_ag…
YehongPan Oct 13, 2026
068dc36
feat(agent-core): select Jiuwen in agent configuration UI
YehongPan Oct 13, 2026
5812d75
fix(agent-core): disable Jiuwen planning in configuration debug
YehongPan Oct 13, 2026
1e9c388
fix(agent-core): replay Jiuwen conversation history as native messages
YehongPan Oct 13, 2026
d4d472d
fix(agent-core): isolate current Jiuwen tasks and upgrade native core
YehongPan Oct 13, 2026
7fd889c
feat(agent-core): integrate Jiuwen with shared Nexent runtime
YehongPan Oct 19, 2026
017eb9a
Merge origin/develop into Jiuwen agent-core integration
YehongPan Oct 19, 2026
193fc90
fix: harden Jiuwen model and output adapters
YehongPan Oct 20, 2026
544d7f4
Merge origin/develop into Jiuwen agent-core integration
YehongPan Oct 21, 2026
f44f78e
fix(jiuwen): complete authorized skills and native resource execution
YehongPan Oct 22, 2026
bf4a066
Merge origin/develop into Jiuwen agent-core integration
YehongPan Oct 27, 2026
f1d515f
Merge latest origin/develop source inspection and pagination fixes
YehongPan Oct 27, 2026
959ebe5
refactor(sdk): migrate agent internals to canonical modules
YehongPan Oct 30, 2026
8f242dd
refactor(agents): share behavior prompts and delegation policy
YehongPan Oct 30, 2026
592ca2c
refactor(sdk): finish canonical imports and regression migration
YehongPan Oct 30, 2026
e16275a
fix(agents): preserve core and context manager on transfer
YehongPan Oct 30, 2026
f5141bd
test(jiuwen): align verification fixtures with shared runtime
YehongPan Oct 30, 2026
8b0bb33
fix(chat): persist retry branches and restore saved history
YehongPan Oct 30, 2026
c6e0db9
Merge latest origin/develop into Jiuwen core integration
YehongPan Nov 2, 2026
d68938e
Merge origin/develop into Jiuwen core integration
YehongPan Nov 5, 2026
3338599
fix(jiuwen): stabilize runtime and workbench creation flows
YehongPan Nov 9, 2026
74514fe
test(jiuwen): compare Skill bindings independently of row order
YehongPan Nov 9, 2026
96e153f
Merge origin/develop into Jiuwen core integration
YehongPan Nov 9, 2026
ee4f86d
test(workbench): cover Jiuwen clarification and restored answers
YehongPan Nov 10, 2026
ea4ac4d
fix(jiuwen): preserve empty and null MCP arguments
YehongPan Nov 10, 2026
e1b361b
fix(workbench): localize live and restored agent action summaries
YehongPan Nov 10, 2026
17e48a1
fix(ci): restore Jiuwen regression and diagnostic safety checks
YehongPan Nov 10, 2026
f08419d
fix(tests): cover authorized Jiuwen Skills and remove merged SQL copies
YehongPan Nov 10, 2026
c68cc45
fix(jiuwen): guarantee cancellation cleanup and cover adapter lifecycle
YehongPan Nov 10, 2026
44915e7
fix(agent): normalize Core output at the backend protocol boundary
YehongPan Nov 11, 2026
84b53f4
fix(deploy): recover Supabase sources and narrow Workbench upgrade sh…
Jasonxia007 Nov 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Nexent output boundary and common conversation presentation

`frontend/lib/nexentOutputAdapter.ts` normalizes public execution envelopes into the existing Nexent event protocol. It creates no React nodes or assistant-ui parts and does not select behavior by Core name. Live SSE and saved units use the same state machine; rendering remains owned by the original chat parsers and components.

- Remove the M5 `runtime-output` renderer path. Reasoning, Markdown, ToolFallback and SubAgentContainer render all Core outputs.
- Ordinary model process text uses the established process event type. Preserve explicit provider reasoning separately in metadata and never invent reasoning text.
- Only a validated, sealed successful RunResult confirms final content. Failed/stopped/missing/repeated/order-invalid results retain partial and never become completed by EOF.
- Correlate tool results by turn, invocation and local ID. Tool error and child execution status are generic presentation inputs for every Core.
- Deserialize envelopes only at the saved-unit boundary. Live model JSON remains ordinary text.
- Unknown token counts remain explicitly unknown in the shared usage component; do not display zero as a substitute.
- Historical rendering uses saved events and persisted status, never the Agent's current Core selection.
42 changes: 27 additions & 15 deletions backend/adapters/jiuwen_sdk_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
"""
openjiuwen SDK adapter for Nexent.

This module works around circular import bugs in openjiuwen 0.1.13 by:
For versions before 0.1.18, this module works around circular imports by:
1. Stubbing react_agent_evolve (causes the circular import)
2. Installing a meta-path finder that blocks broken __init__.py files
3. All of the above runs at MODULE LOAD TIME (before openjiuwen imports)
3. Installing these workarounds before openjiuwen imports

Native versions use their real packages so the Agent Core can coexist with
prompt optimization in the same service process.

The adapters/__init__.py wraps the import in try/except so the server
starts even when openjiuwen is not installed.
Expand All @@ -19,11 +22,19 @@
import os
import sys
import types
from importlib.metadata import PackageNotFoundError, version
from typing import TYPE_CHECKING, Any, List, Literal, Optional, Tuple

if TYPE_CHECKING:
from openjiuwen.core.foundation.llm.schema.config import ModelClientConfig, ModelRequestConfig

from packaging.version import Version

try:
_needs_legacy_bypass = Version(version("openjiuwen")) < Version("0.1.18")
except PackageNotFoundError:
_needs_legacy_bypass = False

# ----------------------------------------------------------------------
# MUST install bypasser + stubs before any openjiuwen import.
# The module-level `from openjiuwen.dev_tools.tune.base import ...` below
Expand All @@ -33,7 +44,7 @@

# Stub react_agent_evolve so single_agent.__init__.py can import it without
# hitting the circular core.operator dependency.
if "openjiuwen.core.single_agent.agents.react_agent_evolve" not in sys.modules:
if _needs_legacy_bypass and "openjiuwen.core.single_agent.agents.react_agent_evolve" not in sys.modules:
_stub = types.ModuleType("openjiuwen.core.single_agent.agents.react_agent_evolve")
_stub.__file__ = "<bypasser stub>"

Expand All @@ -60,15 +71,15 @@ class _DummyReActAgentEvolve: # noqa: N801
("dashscope", {}),
("pdfplumber", {}),
]:
if _name not in sys.modules:
if _needs_legacy_bypass and _name not in sys.modules:
_mod = types.ModuleType(_name)
_mod.__path__ = []
for _k, _v in _attrs.items():
setattr(_mod, _k, _v)
sys.modules[_name] = _mod

for _name in ["pymilvus.client", "pymilvus.client.utils"]:
if _name not in sys.modules:
if _needs_legacy_bypass and _name not in sys.modules:
_m = types.ModuleType(_name)
_m.__path__ = []
if _name == "pymilvus.client.utils":
Expand All @@ -82,7 +93,7 @@ class _DummyReActAgentEvolve: # noqa: N801
("dashscope.common", {"REQUEST_TIMEOUT_KEYWORD": "timeout"}),
("dashscope.common.constants", {"REQUEST_TIMEOUT_KEYWORD": "timeout"}),
]:
if _name not in sys.modules:
if _needs_legacy_bypass and _name not in sys.modules:
_m = types.ModuleType(_name)
_m.__path__ = []
for _k, _v in _attrs.items():
Expand Down Expand Up @@ -180,23 +191,24 @@ def __getattr__(self, name: str) -> Any:
raise AttributeError(name)


# Install the bypasser into sys.meta_path so it intercepts openjiuwen imports.
for _finder in sys.meta_path:
if isinstance(_finder, _JiuwenInitBypasser):
break
else:
sys.meta_path.insert(0, _JiuwenInitBypasser())
# Install the finder only for the versions that require the import workaround.
if _needs_legacy_bypass:
for _finder in sys.meta_path:
if isinstance(_finder, _JiuwenInitBypasser):
break
else:
sys.meta_path.insert(0, _JiuwenInitBypasser())

# No-op: bypasser is already installed above.
# Kept for backward compatibility with code that calls it.
_bypasser_installed = True
_bypasser_installed = _needs_legacy_bypass


def _install_jiuwen_bypasser() -> bool:
"""No-op: bypasser is installed at module load time. Kept for compatibility."""
return True

# Now safe to import openjiuwen — bypasser is active.
# Import native packages directly unless the legacy workaround is required.
from openjiuwen.dev_tools.tune.base import Case as _Case, EvaluatedCase as _EvaluatedCase


Expand Down Expand Up @@ -253,7 +265,7 @@ def run_in_thread():
finally:
new_loop.close()

from nexent.core.agents.run_agent import _get_default_agent_thread_manager
from nexent.core.agents.orchestration.entry import _get_default_agent_thread_manager
from nexent.core.concurrency import ManagedTaskSpec, get_current_thread_manager

manager = get_current_thread_manager() or _get_default_agent_thread_manager()
Expand Down
11 changes: 10 additions & 1 deletion backend/contracts/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Optional as Opt

from nexent.core.agent_names import validate_agent_name
from nexent.core.agents.agent_model import AgentVerificationConfig, ToolConfig
from nexent.core.agents.contracts.config import AgentVerificationConfig, ToolConfig
from pydantic import (
BaseModel,
ConfigDict,
Expand Down Expand Up @@ -190,12 +190,16 @@ def reject_legacy_agent_fields(value):


class AgentRequest(BaseModel):
_runtime_user_message_id: Optional[int] = PrivateAttr(default=None)
_runtime_user_message_index: Optional[int] = PrivateAttr(default=None)
# Internal provenance is never accepted from serialized client requests.
_message_source: str = PrivateAttr(default="CHAT")
query: str
clarification_response: Optional[ClarificationResponse] = None
conversation_id: Optional[int] = None
history: Optional[List[HistoryItem]] = None
retry_user_message_id: Optional[int] = Field(default=None, gt=0, strict=True)
retry_message_index: Optional[int] = Field(default=None, ge=0, strict=True)
# Complete list of attachment information
minio_files: Optional[List[Dict[str, Any]]] = None
agent_id: Optional[int] = None
Expand Down Expand Up @@ -235,6 +239,10 @@ def validate_context_policy(cls, value):

@model_validator(mode="after")
def validate_workbench_entrypoint(self):
if self.retry_message_index is not None and self.retry_user_message_id is None:
raise ValueError("Retry requires an original user message ID")
if self.retry_user_message_id is not None and (not self.conversation_id or self.is_debug):
raise ValueError("Retry requires a persisted conversation")
if (
self.entrypoint == "workbench"
and self.workbench is None
Expand Down Expand Up @@ -442,6 +450,7 @@ class AgentIDRequest(BaseModel):


class ExportAndImportAgentInfo(BaseModel):
enable_context_manager: bool = True
agent_id: int
tenant_id: Optional[str] = None
name: str
Expand Down
2 changes: 1 addition & 1 deletion backend/contracts/clarification.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

def normalize_form(value: dict[str, Any]) -> dict[str, Any]:
"""Apply the runtime form contract without accepting execution fields."""
from nexent.core.agents.clarification import ClarificationForm
from nexent.core.agents.contracts.clarification import ClarificationForm

raw = dict(value)
if type(raw.pop("schema_version", None)) is not int or value["schema_version"] != 1:
Expand Down
8 changes: 8 additions & 0 deletions backend/contracts/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,11 @@ def _build_otlp_headers(headers_str, authorization, x_api_key, langsmith_api_key


return OTLP_HEADERS


def parse_agent_core(value: str) -> str:
"""Validate the deployment Core without reading process configuration."""
core = value.strip().lower()
if core not in {"smolagents", "jiuwen"}:
raise ValueError("NEXENT_AGENT_CORE must be smolagents or jiuwen")
return core
5 changes: 4 additions & 1 deletion backend/contracts/constants/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
from pathlib import Path
from dotenv import load_dotenv
from ..configuration import parse_positive_float, parse_positive_int, parse_port, _require_positive, _build_otlp_headers, _resolve_app_version
from ..configuration import parse_agent_core, parse_positive_float, parse_positive_int, parse_port, _require_positive, _build_otlp_headers, _resolve_app_version

# Environment initialization
# Repository-root callers import this module through the backend namespace.
Expand All @@ -13,6 +13,9 @@
_ENVIRONMENT_LOADED = load_dotenv(override=False)
_CONFIG_DIRECTORY = Path(__file__).resolve().parents[2] / 'configs'

# One deployment owns the execution Core; persisted Agent fields are legacy metadata.
NEXENT_AGENT_CORE = parse_agent_core(os.getenv('NEXENT_AGENT_CORE', 'smolagents'))

# Operations service configuration
OPS_PORT = parse_port(os.getenv('OPS_PORT'), 'OPS_PORT', 5016)

Expand Down
1 change: 0 additions & 1 deletion backend/contracts/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,3 @@ def decode_agent_conversation_cursor(
return activity.astimezone(timezone.utc), value[1]
except (ValueError, TypeError, UnicodeError, OverflowError) as exc:
raise ValueError("Invalid agent conversation cursor") from exc

Loading
Loading