Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ assets/
.Trashes
ehthumbs.db
Thumbs.db

# Task-local development artifacts and connection metadata.
.runtime/
2 changes: 2 additions & 0 deletions backend/agents/create_agent_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
from management.services.mcp.addressing import get_tenant_local_mcp_server
from infrastructure.redis import get_redis_client
from consts.const import (
NEXENT_AGENT_RUNTIME,
AGENT_WORKSPACE_ROOT,
A2A_AGENT_INVOCATION_TIMEOUT_SECONDS,
NEXENT_SANDBOX_WORKSPACE_MODE,
Expand Down Expand Up @@ -2910,6 +2911,7 @@ async def create_agent_run_info(
})

agent_run_info = AgentRunInfo(
agent_runtime=NEXENT_AGENT_RUNTIME,
query=final_query,
model_config_list=model_list,
observer=MessageObserver(lang=language),
Expand Down
4 changes: 4 additions & 0 deletions backend/consts/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
# avoids silently replacing operator-provided service addresses.
load_dotenv(override=False)

NEXENT_AGENT_RUNTIME = os.getenv("NEXENT_AGENT_RUNTIME", "codeagent").strip()
if NEXENT_AGENT_RUNTIME not in ("codeagent", "toolcalling"):
raise ValueError("NEXENT_AGENT_RUNTIME must be codeagent or toolcalling")


# TODO: Analyze every variable if this is used
# Test voice file path (WAV format for volcengine STT)
Expand Down
1 change: 1 addition & 0 deletions backend/services/nl2agent_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,7 @@ async def build_nl2agent_run_info(

stop_event = threading.Event()
run_info = AgentRunInfo(
agent_runtime="codeagent", # Platform authoring prompts retain their closed CodeAgent protocol.
query=final_query,
model_config_list=model_config_list,
observer=_Nl2AgentBoundaryObserver(
Expand Down
1 change: 1 addition & 0 deletions backend/services/nl2skill_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ async def build_nl2skill_run_info(
compaction_target_tokens=context_budget.compaction_target_tokens if context_budget else 0,
)
run_info = AgentRunInfo(
agent_runtime="codeagent", # Platform authoring prompts retain their closed CodeAgent protocol.
query=template.get("user_prompt") or final_query,
model_config_list=model_config_list,
observer=MessageObserver(lang=template_language),
Expand Down
3 changes: 3 additions & 0 deletions deploy/env/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,6 @@ NEXENT_LITELLM_JSON=/opt/nexent/litellm_models.json
# MODEL_REASONING_WIRE_PROFILES_PATH=/opt/backend/configs/model_reasoning_wire_profiles.json
# Short-lived structural diagnostics only; generated text and tool arguments are always redacted.
NEXENT_MODEL_DIAGNOSTIC_CHUNKS=0

# Agent runtime: codeagent (stable default) or toolcalling (experimental).
NEXENT_AGENT_RUNTIME=codeagent
40 changes: 40 additions & 0 deletions docs/experimental-native-runtime/develop-migration-roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Develop migration roadmap

## Validated observations

The SDK and deployed application experiments demonstrate that an existing Nexent tool can execute through native model APIs and return a correlated result used by the next generation. This does not establish full harness equivalence. The source contains CodeAgent-specific wrapper checks, prompt policies and textual history assumptions beyond the agent constructor. Upgrading to inspected smolagents 1.26.0 alone does not remove textual history rendering.

## Recommendations

1. Define a small runtime lifecycle contract around construction, invocation, cancellation, state/context input and observer output. Keep CodeAgent and ToolCallingAgent loops independent.
2. Keep definition-specific authorized tool construction and business implementations shared. Make validation/execution context and result identity explicit so native and sandbox bridge calls agree.
3. Use a first-class code_execution tool backed by the existing sandbox lifecycle. Preserve per-invocation kernels, bridge permissions and limits. Do not infer full compatibility from arithmetic execution alone.
4. Represent native assistant calls and per-ID results as indivisible context groups. Add budget accounting and group-level compaction before production rollout; request-tail preservation with fail-on-overflow is an experimental shortcut.
5. Separate semantic instructions from runtime formatting at prompt composition. Retain authored text, replace only platform-owned CodeAgent conventions, and surface incompatible definitions explicitly.
6. Specify provider capabilities independently for tool calling, streaming, reasoning parameters and continuation metadata. Validate representative Qwen, DeepSeek, GLM and MiniMax configurations rather than assuming family equivalence.
7. Port the tested planning, verification/guardrails and clarification adapters and add live policy/form journeys before claiming parity. Never turn explicit experimental rejection into silent production bypass.
8. Maintain native event IDs across model, executor, nested bridge and observer/tracing boundaries; reconcile persisted message-unit projection and frontend replay.

## Suggested implementation sequence on develop

Prerequisites: retain this experiment's hybrid/sandbox/provider/MCP/delegation evidence and stable regressions, then close the explicitly unverified harness boundaries. Reconcile develop's newer backend configuration layout before implementation; do not copy v2.8.4 const.py paths blindly.

Introduce lifecycle/configuration contract with stable-default tests; native wire model adapter; direct tool registry; sandbox code tool/bridge; protocol-preserving context; observer/history persistence; managed agents/MCP; planning/verification/clarification; provider capability coverage. Keep each stage selectable and independently testable.

Use unit checks for schemas/IDs/configuration/error/cancellation branches, real provider and sandbox D3 cases for actual protocol exchanges, existing definition API/browser journeys, and branch-built deployment/rollback D5 checks. Retain source/image identities and sanitized Phoenix span evidence per acceptance scenario.

Rollout remains opt-in. Rollback selects codeagent and restarts application without database migration. Avoid production migration until all required harness and compatibility checks pass.

## Unresolved questions

Native current-run compaction and overflow recovery; model tool limits/schema portability and provider continuation metadata; sandbox bridge equivalence for dynamic MCP containers; concurrent managed-agent lifetime; multimodal native messages/results; live planning/verifier/clarification journeys; statistically meaningful token/latency comparisons. These are open work, not validated improvements.

## Concrete migration lessons

- Retain the independent loops, native wire adapter and shared authorized tool construction; do not copy CoreAgent parsing or its textual history.
- Make executor registration and per-execution context explicit lifecycle operations. Upstream automatic registration is unsafe for the optional code tool.
- Preserve callback context across the Python bridge, including tool-call identity, permissions and trace parentage. The final Docker evidence shows the programmatic existing-tool span beneath code_execution.
- Model thinking toggles and effort independently. DeepSeek required an explicit thinking object in the tested gateway, while DeepSeek/MiniMax reasoning fields varied across gateway runs. Native tool support is a separate observation.
- Use capability-based adapters for plan/verification/clarification and authoring helpers; the spike's borrowed CoreAgent plan methods are a temporary coupling to replace.
- Define atomic native exchange groups for context compaction and a multimodal message algebra before production rollout.
- Preserve default CodeAgent as the rollback path. Both selected runtimes completed the same persisted-definition workloads; the isolated timing observations are not a performance benchmark.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"agent_id": 2,
"semantic_fields": [
"name",
"display_name",
"description",
"model_ids",
"max_steps",
"requested_output_tokens",
"prompt",
"enable_context_manager",
"verification_config",
"context_policy",
"model_params_override",
"tools",
"sub_agent_id_list"
],
"sha256_before": "4b8b103aa8b3da25004616c82ba8463c39165a9fad3106dd026c264b9fa8d869",
"sha256_after": "4b8b103aa8b3da25004616c82ba8463c39165a9fad3106dd026c264b9fa8d869",
"browser": "Codex built-in browser",
"verification": "manual browser journey, separate from automated formal runner"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"scenario": "Unauthenticated runtime request",
"status": 500,
"passed": false,
"response": "{\"message\":\"No authorization header provided\"}"
}
44 changes: 44 additions & 0 deletions docs/experimental-native-runtime/evidence/formal-d2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"run_id": "20261109T081518091837Z-NATIVE-RUNTIME-D2",
"status": "PASS",
"records": [
{
"scenario": "plain",
"agent_id": 2,
"model_id": 1,
"definition_sha256_before": "2d40ce0b669d52cb397da6fa32a596c0639a0437b1d18a89a1ae2c4a2baf39f7",
"definition_sha256_after": "2d40ce0b669d52cb397da6fa32a596c0639a0437b1d18a89a1ae2c4a2baf39f7",
"event_types": [
"final_answer",
"model_attempt_control",
"model_output_thinking",
"step_count",
"token_count"
],
"actual_python_observation": false,
"expected_marker": "NATIVE_RUNTIME_OK"
},
{
"status_code": 500,
"denied_before_stream": true
},
{
"scenario": "python",
"agent_id": 2,
"model_id": 1,
"definition_sha256_before": "2d40ce0b669d52cb397da6fa32a596c0639a0437b1d18a89a1ae2c4a2baf39f7",
"definition_sha256_after": "2d40ce0b669d52cb397da6fa32a596c0639a0437b1d18a89a1ae2c4a2baf39f7",
"event_types": [
"execution_logs",
"final_answer",
"model_attempt_control",
"model_output_thinking",
"step_count",
"token_count",
"tool"
],
"actual_python_observation": true,
"expected_marker": "323"
}
]
}
37 changes: 37 additions & 0 deletions docs/experimental-native-runtime/evidence/formal-d5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"run_id": "20261109T081530917315Z-NATIVE-RUNTIME-D5",
"status": "PASS",
"records": [
{
"containers": {
"nexent-runtime": {
"container_id": "d8ddad6561c6e026cfa39abe54b27abbb8fa7db44462aabc0f24a5222b4498a2",
"image_id": "sha256:cd0e5604e99984eb0f21db8c01703100861387183299538f923247f0b37a51e5"
},
"nexent-config": {
"container_id": "eaa6911721391b2d7d7f189fe4bc5fe24e83fb6fa2fd4034720f6d1502ee4aa2",
"image_id": "sha256:cd0e5604e99984eb0f21db8c01703100861387183299538f923247f0b37a51e5"
},
"nexent-northbound": {
"container_id": "2734ed74f033071aeb61229caa95aebdfa2c21d39d509b4e9b91b80eab17303e",
"image_id": "sha256:cd0e5604e99984eb0f21db8c01703100861387183299538f923247f0b37a51e5"
},
"nexent-mcp": {
"container_id": "55bd2cba466abdfbb5c2be870641d085b28504574a90a2f271fccbbefffb3b80",
"image_id": "sha256:cd0e5604e99984eb0f21db8c01703100861387183299538f923247f0b37a51e5"
},
"nexent-web": {
"container_id": "77d7bcaa0ea5736d763d1b1a0168374a61483f91851c898e917221ca69ea6ef4",
"image_id": "sha256:12ae7d92874641da2cf43d9f7c8758b3baba10aa7bd316bf1a644603caeae161"
}
},
"runtime_source": {
"runtime": "toolcalling",
"sha256": "d306935846a10a44a0cb0f5e4d1208f53198600630fef8136fbdf8e073bd3bb4"
},
"sandbox_image_id": "sha256:378770c0d540f65b553741a2d81dfa93055ace3f5eace175772dd68952aa19ae",
"runtime_source_mount_absent": true,
"application_and_auth_health": true
}
]
}
45 changes: 45 additions & 0 deletions docs/experimental-native-runtime/evidence/host-api.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"ids": {
"tenant_id": "5484234d-839b-488c-8c66-c4ee4b858314",
"model_id": 1,
"model_name": "qwen3.8-flash",
"agent_id": 2
},
"definition_sha256_before": "ea6911db7b90ba9f7a7076fd5b479a6c30d1dda1557d8f469a1ce18bc0867fd5",
"definition_sha256_after": "ea6911db7b90ba9f7a7076fd5b479a6c30d1dda1557d8f469a1ce18bc0867fd5",
"runs": [
{
"scenario": "plain",
"event_types": [
"final_answer",
"model_attempt_control",
"model_output_thinking",
"step_count",
"token_count"
],
"expected_marker": "NATIVE_RUNTIME_OK",
"elapsed_seconds": 2.018,
"event_count": 7
},
{
"scenario": "python",
"event_types": [
"execution_logs",
"final_answer",
"model_attempt_control",
"model_output_thinking",
"step_count",
"token_count",
"tool"
],
"expected_marker": "323",
"elapsed_seconds": 5.569,
"event_count": 13
},
{
"scenario": "unauthenticated",
"status_code": 500,
"denied": true
}
]
}
Loading
Loading