|
43 | 43 | from ...kernel.actors import get_effective_role, list_actors |
44 | 44 | from ...kernel.group import load_group |
45 | 45 | from ...kernel.query_projections import get_actor_list_projection |
| 46 | +from ...kernel.pet_actor import PET_ACTOR_ID |
46 | 47 | from ...kernel.ledger import append_event |
47 | 48 | from ...kernel.prompt_files import ( |
48 | 49 | HELP_FILENAME, |
@@ -77,6 +78,43 @@ def _error(code: str, message: str, *, details: Optional[Dict[str, Any]] = None) |
77 | 78 | return DaemonResponse(ok=False, error=DaemonError(code=code, message=message, details=(details or {}))) |
78 | 79 |
|
79 | 80 |
|
| 81 | +_PET_FORBIDDEN_CONTEXT_OPS = { |
| 82 | + "coordination.brief.update", |
| 83 | + "coordination.note.add", |
| 84 | + "task.create", |
| 85 | + "task.update", |
| 86 | + "task.move", |
| 87 | + "task.restore", |
| 88 | + "task.delete", |
| 89 | + "meta.merge", |
| 90 | + "role_notes.set", |
| 91 | + "agent_state.clear", |
| 92 | +} |
| 93 | + |
| 94 | + |
| 95 | +def _check_pet_context_permission(by: str, op_name: str, *, target_actor_id: Optional[str] = None) -> Optional[str]: |
| 96 | + if str(by or "").strip() != PET_ACTOR_ID: |
| 97 | + return None |
| 98 | + if op_name in _PET_FORBIDDEN_CONTEXT_OPS: |
| 99 | + return f"Permission denied: pet cannot run {op_name}" |
| 100 | + if op_name == "agent_state.update": |
| 101 | + target = str(target_actor_id or "").strip() |
| 102 | + if target and target != PET_ACTOR_ID: |
| 103 | + return f"Permission denied: pet can only update its own agent_state ({PET_ACTOR_ID})" |
| 104 | + return None |
| 105 | + |
| 106 | + |
| 107 | +def _validate_pet_agent_state_update(raw: Dict[str, Any], *, actor_id: str) -> Optional[str]: |
| 108 | + target = str(actor_id or "").strip() |
| 109 | + if target != PET_ACTOR_ID: |
| 110 | + return f"Permission denied: pet can only update its own agent_state ({PET_ACTOR_ID})" |
| 111 | + allowed = {"op", "actor_id", "agent_id", "user_model", "user_profile"} |
| 112 | + extra = sorted(key for key in raw.keys() if key not in allowed) |
| 113 | + if extra: |
| 114 | + return "Permission denied: pet agent_state.update only allows user_model; disallowed keys: " + ", ".join(extra) |
| 115 | + return None |
| 116 | + |
| 117 | + |
80 | 118 | def _status_value(value: Any) -> str: |
81 | 119 | if isinstance(value, Enum): |
82 | 120 | return str(value.value or "") |
@@ -545,6 +583,10 @@ def _check_permission( |
545 | 583 | if group is None: |
546 | 584 | return None |
547 | 585 |
|
| 586 | + pet_err = _check_pet_context_permission(by, op_name, target_actor_id=target_actor_id) |
| 587 | + if pet_err: |
| 588 | + return pet_err |
| 589 | + |
548 | 590 | role = "user" if by == "user" else get_effective_role(group, by) |
549 | 591 | if role in {"user", "foreman"}: |
550 | 592 | if op_name in {"agent_state.update", "agent_state.clear"} and target_actor_id and by not in {"system", target_actor_id}: |
@@ -1503,6 +1545,10 @@ def _mark_change(index: int, op_name: str, detail: str, *, task_id: str = "") -> |
1503 | 1545 | perm_err = _check_permission(by, op_name, group_id, target_actor_id=actor_id) |
1504 | 1546 | if perm_err: |
1505 | 1547 | raise ValueError(perm_err) |
| 1548 | + if by == PET_ACTOR_ID: |
| 1549 | + pet_update_err = _validate_pet_agent_state_update(raw, actor_id=actor_id) |
| 1550 | + if pet_update_err: |
| 1551 | + raise ValueError(pet_update_err) |
1506 | 1552 | agent = _get_or_create_agent(agents_state, actor_id) |
1507 | 1553 | updated = False |
1508 | 1554 | hot = agent.hot if isinstance(agent.hot, AgentStateHot) else AgentStateHot() |
@@ -1583,6 +1629,10 @@ def _mark_change(index: int, op_name: str, detail: str, *, task_id: str = "") -> |
1583 | 1629 | perm_err = _check_permission(by, op_name, group_id, target_actor_id=actor_id) |
1584 | 1630 | if perm_err: |
1585 | 1631 | raise ValueError(perm_err) |
| 1632 | + if by == PET_ACTOR_ID: |
| 1633 | + pet_update_err = _validate_pet_agent_state_update(raw, actor_id=actor_id) |
| 1634 | + if pet_update_err: |
| 1635 | + raise ValueError(pet_update_err) |
1586 | 1636 | agent = _get_or_create_agent(agents_state, actor_id) |
1587 | 1637 | agent.hot = AgentStateHot() |
1588 | 1638 | agent.warm = AgentStateWarm() |
|
0 commit comments