feat: harden company resume, external sessions, and Office UI i18n

Improve suspend/resume identity continuity and external broker session handling, add Office UI internationalization with rebuilt assets, and expand company org configs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
小东
2026-07-16 09:20:40 +08:00
parent eb934cf2bd
commit cb6b8ed6e1
32 changed files with 2562 additions and 446 deletions
+47
View File
@@ -920,6 +920,53 @@ class CompanyCollaborationTests(unittest.IsolatedAsyncioTestCase):
any(item.startswith("[Workspace] Prepared workspace roots:") for item in task.metadata.get("progress_log", []))
)
async def test_failed_work_item_transitions_out_of_running_phase(self) -> None:
execute_task = AsyncMock(
return_value=TaskResult(status=TaskStatus.FAILED, content="provider limit")
)
save_task = AsyncMock()
executor = CompanyWorkItemExecutor(
org_engine=DummyOrgEngine(),
communication=SimpleNamespace(),
approval_engine=SimpleNamespace(),
memory=None,
execute_task=execute_task,
save_task=save_task,
)
task = Task(
id="failed-work-item-task",
title="Failed Work Item",
assigned_to="executor",
status=TaskStatus.PENDING,
metadata={
"work_item_projection_id": "failed_execution",
"work_item_role_id": "executor",
"work_item_turn_type": "execute",
"work_item_execution_strategy": "native",
"progress_log": [],
},
)
with patch(
"opc.layer2_organization.company_mode.transition_work_item_from_task",
new_callable=AsyncMock,
) as transition:
result = await executor._run_work_item(task, {task.id: task, "failed_execution": task})
self.assertEqual(result.status, TaskStatus.FAILED)
self.assertTrue(
any(
call.kwargs.get("target_status_or_phase") == TaskStatus.FAILED
and call.kwargs.get("reason") == "work_item_failed"
for call in transition.await_args_list
)
)
self.assertIn(
"Work item ended with status failed.",
task.metadata.get("progress_log", []),
)
save_task.assert_awaited()
async def test_workspace_bootstrap_work_item_records_manifest_and_reserved_layout(self) -> None:
with _workspace_tempdir() as tmpdir:
workspace = tmpdir / "runtime-root"
@@ -276,6 +276,154 @@ class CompanyRuntimeSuspendResumeTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(refreshed_item.metadata.get("dispatch_hold"), "")
self.assertEqual(refreshed_item.claimed_by_role_runtime_session_id, "")
async def test_continue_merges_task_suspended_after_checkpoint_creation(self) -> None:
store = await self._store()
plan, task = await self._seed_runtime(store)
engine = self._engine(store)
await engine.suspend_company_runtime(
origin_task_id=task.id,
session_id="sess-parent",
reason="startup_interrupted",
)
review_item = DelegationWorkItem(
work_item_id="review-item-v3",
run_id="run-1",
role_id="executor",
seat_id="seat-1",
title="Review v3",
summary="Review the completed report.",
kind="review",
projection_id="review-v3",
phase=Phase.RUNNING,
metadata={
"work_item_projection_id": "review-v3",
"dispatch_hold": "company_runtime_suspended",
"suspend_checkpoint_type": "company_runtime_interrupted",
"suspended_phase": Phase.RUNNING.value,
},
)
await store.save_delegation_work_item(review_item)
review_task = Task(
id="review-task-v3",
title="Review v3",
session_id="sess-review-v3",
parent_session_id="sess-parent",
status=TaskStatus.RUNNING,
project_id="proj1",
assigned_to="executor",
assigned_external_agent="codex",
metadata={
"company_profile": "corporate",
"execution_model": "multi_team_org",
"runtime_model": "multi_team_org",
"work_item_runtime": True,
"work_item_projection_id": "review-v3",
"delegation_run_id": "run-1",
"delegation_role_session_id": "role-runtime-1",
"company_work_item_plan": serialize_company_work_item_runtime_plan(plan),
"company_runtime_stop_state": "suspended",
"company_runtime_suspend_checkpoint_type": "company_runtime_interrupted",
"company_runtime_suspended_at": datetime.now().isoformat(),
"dispatch_hold": "company_runtime_suspended",
"suspended_task_status": TaskStatus.RUNNING.value,
},
)
set_linked_work_item_id(review_task, review_item.work_item_id)
await store.save_task(review_task)
await store.link_work_item_runtime_task(review_item.work_item_id, review_task.id)
captured: dict[str, Any] = {}
class DummyCompanyExecutor:
async def execute(
self,
resumed_plan: CompanyWorkItemRuntimePlan,
tasks: list[Task],
) -> str:
captured["plan"] = resumed_plan
captured["tasks"] = tasks
return "runtime resumed"
engine.company_executor = DummyCompanyExecutor()
await engine._maybe_resume_checkpoint(
"continue",
"sess-parent",
reply_metadata={"ui_force_resume": True},
)
resumed_ids = {item.id for item in captured["tasks"]}
refreshed_review_item = await store.get_delegation_work_item(
review_item.work_item_id
)
resolved = await store.get_execution_checkpoints(
project_id="proj1",
session_id="sess-parent",
checkpoint_types=["company_runtime_suspended"],
statuses=["resolved"],
)
self.assertEqual(resumed_ids, {task.id, review_task.id})
assert refreshed_review_item is not None
self.assertEqual(refreshed_review_item.metadata.get("dispatch_hold"), "")
self.assertEqual(len(resolved), 1)
self.assertEqual(
set(resolved[0].payload.get("task_ids", [])),
{task.id, review_task.id},
)
self.assertEqual(
{
snapshot.get("task_id")
for snapshot in resolved[0].payload.get("task_snapshots", [])
},
{task.id, review_task.id},
)
async def test_runtime_refreshes_canonical_adapter_state_before_persisting(self) -> None:
store = await self._store()
_, task = await self._seed_runtime(store)
role_session_id = "role-runtime-1"
stale_state = {
"selected_execution_agent": "codex",
"external_resume_session_id": "thread-terminal",
"external_resume_agent_type": "codex",
"codex": {
"resume_session_id": "thread-terminal",
"provider_session_id": "thread-terminal",
},
}
role_session = await store.get_delegation_role_session(role_session_id)
assert role_session is not None
role_session.adapter_session_state = dict(stale_state)
await store.save_delegation_role_session(role_session)
runtime = CompanyRuntime(org_engine=None, communication=None, store=store)
runtime.role_sessions[role_session_id] = role_session
member = CompanyMemberSession(
member_session_id="member-operations",
role_session_id=role_session_id,
role_id="executor",
employee_id="employee-1",
adapter_session_state=dict(stale_state),
)
await store.update_role_session_adapter_state(
role_session_id,
"codex",
None,
)
await runtime._refresh_adapter_session_state_from_store(member)
self.assertNotIn("codex", member.adapter_session_state)
self.assertNotIn(
"external_resume_session_id",
member.adapter_session_state,
)
self.assertEqual(
runtime.role_sessions[role_session_id].adapter_session_state,
member.adapter_session_state,
)
async def test_second_stop_during_resumed_execution_restores_pending_checkpoint(self) -> None:
store = await self._store()
_, task = await self._seed_runtime(store)
+72 -3
View File
@@ -2513,7 +2513,8 @@ class ExternalAgentMonitoringTests(unittest.IsolatedAsyncioTestCase):
def test_codex_adapter_resolves_launch_binary_before_spawn(self) -> None:
metadata: dict[str, object] = {}
with patch("opc.layer3_agent.adapters.base.shutil.which", return_value=r"C:\Tools\codex.cmd"):
with patch("opc.layer3_agent.adapters.base.os.name", "posix"), \
patch("opc.layer3_agent.adapters.base.shutil.which", return_value=r"C:\Tools\codex.cmd"):
cmd = CodexAdapter._resolve_launch_command(
["codex", "exec", "--json", "-"],
launch_metadata=metadata,
@@ -2523,6 +2524,39 @@ class ExternalAgentMonitoringTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(metadata["configured_binary"], "codex")
self.assertEqual(metadata["resolved_binary"], r"C:\Tools\codex.cmd")
def test_codex_adapter_wraps_windows_cmd_shim_before_spawn(self) -> None:
metadata: dict[str, object] = {}
which_results = {
"codex": r"D:\nodejs\codex",
"cmd.exe": r"C:\Windows\System32\cmd.exe",
}
with patch("opc.layer3_agent.adapters.base.os.name", "nt"), \
patch("opc.layer3_agent.adapters.base.os.path.isfile", side_effect=lambda path: path == r"D:\nodejs\codex.cmd"), \
patch("opc.layer3_agent.adapters.base.shutil.which", side_effect=lambda name, path=None: which_results.get(name)), \
patch.dict(os.environ, {"COMSPEC": r"C:\Windows\System32\cmd.exe"}, clear=False):
cmd = CodexAdapter._resolve_launch_command(
["codex", "exec", "--json", "-"],
launch_metadata=metadata,
)
self.assertEqual(
cmd,
[
r"C:\Windows\System32\cmd.exe",
"/d",
"/s",
"/c",
r"D:\nodejs\codex.cmd",
"exec",
"--json",
"-",
],
)
self.assertEqual(metadata["configured_binary"], "codex")
self.assertEqual(metadata["resolved_binary"], r"D:\nodejs\codex.cmd")
self.assertEqual(metadata["launch_wrapper"], r"C:\Windows\System32\cmd.exe")
def test_codex_adapter_resolves_launch_binary_with_extra_env_path(self) -> None:
with patch("opc.layer3_agent.adapters.base.shutil.which", return_value=r"D:\bin\codex.cmd") as which_mock:
CodexAdapter._resolve_launch_command(
@@ -2532,6 +2566,40 @@ class ExternalAgentMonitoringTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(which_mock.call_args.kwargs["path"], r"D:\bin")
def test_codex_adapter_forwards_only_openopc_runtime_env_to_shell_policy(self) -> None:
metadata: dict[str, object] = {}
cmd = CodexAdapter._inject_runtime_shell_environment(
["codex", "exec", "--json", "-"],
{
"OPC_ALLOWED_COLLAB_TOOLS": json.dumps(["delegate_work", "inbox"]),
"OPC_COLLAB_RPC_TOKEN": "rpc-token",
"PYTHONPATH": r"D:\OpenOPC",
"UNRELATED_SECRET": "do-not-forward",
},
launch_metadata=metadata,
)
self.assertEqual(cmd[:2], ["codex", "exec"])
config_values = [cmd[index + 1] for index, value in enumerate(cmd[:-1]) if value == "-c"]
self.assertIn(
"shell_environment_policy.set.OPC_ALLOWED_COLLAB_TOOLS='''delegate_work,inbox'''",
config_values,
)
self.assertIn(
"shell_environment_policy.set.OPC_COLLAB_RPC_TOKEN='''rpc-token'''",
config_values,
)
self.assertIn(
"shell_environment_policy.set.PYTHONPATH='''D:\\OpenOPC'''",
config_values,
)
self.assertFalse(any("UNRELATED_SECRET" in value for value in config_values))
self.assertEqual(
metadata["runtime_shell_environment_keys"],
["OPC_ALLOWED_COLLAB_TOOLS", "OPC_COLLAB_RPC_TOKEN", "PYTHONPATH"],
)
def test_codex_adapter_full_auto_uses_dangerous_bypass_flag(self) -> None:
adapter = CodexAdapter(
config=ExternalAgentConfig(command="codex", approval_mode="full-auto")
@@ -5242,10 +5310,11 @@ class ExternalAgentMonitoringTests(unittest.IsolatedAsyncioTestCase):
with tempfile.TemporaryDirectory() as tmpdir:
store = _SessionStoreStub()
adapter = _CollabSurfaceScriptAdapter("print('company-mode external run')\n")
broker = ExternalAgentBroker(store, _ApprovalStub())
from opc.layer2_organization.org_engine import OrgEngine
from opc.core.config import OPCConfig
broker.org_engine = OrgEngine(OPCConfig(), Path(tmpdir))
org_engine = OrgEngine(OPCConfig(), Path(tmpdir))
broker = ExternalAgentBroker(store, _ApprovalStub(), org_engine=org_engine)
self.assertIs(broker.org_engine, org_engine)
task = Task(
id="collab-env-ceo",
title="collab env ceo",
+141
View File
@@ -21,6 +21,7 @@ from __future__ import annotations
import tempfile
import unittest
from datetime import datetime, timedelta
from pathlib import Path
from types import SimpleNamespace
@@ -37,6 +38,7 @@ from opc.core.models import (
from opc.database.store import OPCStore
from opc.layer3_agent.adapters.base import ExternalAgentAdapter
from opc.layer3_agent.external_broker import ExternalAgentBroker
from opc.layer3_agent.external_session_identity import select_best_external_resume_session
# ── Store helper unit tests ──────────────────────────────────────────────
@@ -97,9 +99,28 @@ class AdapterSessionStateHelperTests(unittest.IsolatedAsyncioTestCase):
await self.store.update_role_session_adapter_state(
self.sid, "codex", {"resume_session_id": "thread-abc"},
)
role_session = await self.store.get_delegation_role_session(self.sid)
role_session.adapter_session_state.update({
"selected_execution_agent": "codex",
"external_resume_session_id": "thread-abc",
"external_resume_session_scope_id": "sess-a",
"external_resume_agent_type": "codex",
"resume_source_session": "thread-abc",
})
await self.store.save_delegation_role_session(role_session)
await self.store.update_role_session_adapter_state(self.sid, "codex", None)
entry = await self.store.get_role_session_adapter_state(self.sid, "codex")
refreshed = await self.store.get_delegation_role_session(self.sid)
self.assertIsNone(entry)
self.assertEqual(
refreshed.adapter_session_state.get("selected_execution_agent"),
"codex",
)
self.assertNotIn(
"external_resume_session_id",
refreshed.adapter_session_state,
)
self.assertNotIn("resume_source_session", refreshed.adapter_session_state)
async def test_missing_session_returns_false(self) -> None:
ok = await self.store.update_role_session_adapter_state(
@@ -245,6 +266,49 @@ class BrokerPersistWritesRoleStateTests(unittest.IsolatedAsyncioTestCase):
)
self.assertIsNone(entry)
async def test_failed_resume_clears_matching_older_role_token_and_task_metadata(self) -> None:
await self._persist_run(
task_id="task-old",
agent_type="codex",
resume_session_id="thread-full",
)
adapter = _MiniAdapter(agent_type="codex")
adapter.config.session_mode = "resume"
adapter.config.session_id = "thread-full"
task = self._task(task_id="task-retry")
task.metadata.update({
"external_resume_session_id": "thread-full",
"external_resume_session_scope_id": "sess-a",
"external_resume_agent_type": "codex",
})
await self.broker._persist_session(
adapter=adapter,
task=task,
workspace_path="/tmp/ws",
metadata={
"command": "codex exec resume",
"model": "(cli default)",
"resume_session_id": "thread-full",
},
result=TaskResult(
status=TaskStatus.FAILED,
content="context window full",
artifacts={"resume_session_id": "thread-full"},
),
)
self.assertIsNone(
await self.store.get_role_session_adapter_state(
self.role_session_id,
"codex",
)
)
self.assertNotIn("external_resume_session_id", task.metadata)
self.assertEqual(
task.metadata.get("external_resume_fallback"),
"provider_terminal_failure",
)
async def test_consecutive_tasks_overwrite_with_latest_token(self) -> None:
await self._persist_run(
task_id="task-1", agent_type="codex", resume_session_id="thread-1",
@@ -258,6 +322,34 @@ class BrokerPersistWritesRoleStateTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(entry["resume_session_id"], "thread-2")
self.assertEqual(entry["last_task_id"], "task-2")
def test_newer_failed_row_vetoes_older_done_row_for_same_token(self) -> None:
now = datetime.now()
older = ExternalSession(
agent_type="codex",
project_id="proj1",
session_id="thread-full",
status="done",
metadata={"resume_session_id": "thread-full"},
updated_at=now - timedelta(minutes=1),
)
newer = ExternalSession(
agent_type="codex",
project_id="proj1",
session_id="thread-full",
status="failed",
metadata={"resume_session_id": "thread-full"},
updated_at=now,
)
selected, token = select_best_external_resume_session(
[older, newer],
agent_type="codex",
project_id="proj1",
)
self.assertIsNone(selected)
self.assertEqual(token, "")
async def test_per_agent_isolation_codex_claude_opencode(self) -> None:
# All three external adapters land in the same broker path. Each
# gets its own slot under adapter_session_state keyed by agent_type.
@@ -430,6 +522,55 @@ class BrokerRestorePrefersRoleStateTests(unittest.IsolatedAsyncioTestCase):
"codex",
))
async def test_restore_rejects_terminal_normal_role_token_even_when_preconfigured(self) -> None:
token = "thread-terminal"
await self.store.update_role_session_adapter_state(
self.role_session_id,
"codex",
{
"resume_session_id": token,
"provider_session_id": token,
"last_task_id": "task-old",
},
)
await self.store.save_external_session(ExternalSession(
agent_type="codex",
project_id="proj1",
session_id=token,
opc_session_id=self.role_session_id,
task_id="task-failed",
workspace_path="/tmp/ws",
run_mode="exec",
status="failed",
metadata={
"resume_session_id": token,
"provider_session_id": token,
},
))
adapter = _MiniAdapter(agent_type="codex", can_resume_blank=False)
adapter.config.session_mode = "resume"
adapter.config.session_id = token
task = self._task()
task.metadata.update({
"external_resume_session_id": token,
"external_resume_session_scope_id": "sess-a",
"external_resume_agent_type": "codex",
})
await self.broker._restore_session_resume_from_store(adapter, task)
self.assertEqual(adapter.config.session_mode, "new")
self.assertEqual(adapter.config.session_id, "")
self.assertIsNone(await self.store.get_role_session_adapter_state(
self.role_session_id,
"codex",
))
self.assertNotIn("external_resume_session_id", task.metadata)
self.assertEqual(
task.metadata.get("external_resume_fallback"),
"provider_terminal_failure",
)
if __name__ == "__main__":
unittest.main()
+26
View File
@@ -164,6 +164,32 @@ def _make_review_task(
class VerdictParseRetryTests(unittest.IsolatedAsyncioTestCase):
async def test_terminal_failed_review_card_can_record_parse_failure_outcome(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
store = OPCStore(root / "tasks.db")
await store.initialize()
try:
executor = _build_executor(store, _make_org_engine(root))
review = _make_review_card(review_card_id="review-failed")
review.phase = Phase.FAILED
await store.save_delegation_work_item(review)
persisted = await executor._persist_terminal_review_card(
"review-failed",
phase=Phase.CANCELLED,
outcome="verdict_parse_failed",
)
self.assertIsNotNone(persisted)
self.assertEqual(persisted.phase, Phase.FAILED)
self.assertEqual(
persisted.metadata.get("review_work_item_outcome"),
"verdict_parse_failed",
)
finally:
await store.close()
async def test_unparseable_verdict_spawns_retry_with_hint(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)