mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 03:23:23 +08:00
- Add SQLAlchemy Core repos, pg adapter/compat, assistant_store factory, Alembic bootstrap and migration/cutover scripts. - Harden chat_message writes (NUL scrub for PG), turn_uuid on attempt failure, WS turn_runner fallbacks and gateway executed_turn_uuid init. - start_gateway: log paths, PS7 stderr handling via cmd, background stdout/stderr redirect; runtime assistant_runtime_log_dir export. - Ops: clear_all_chat_sessions with PG-only --postgresql and env-gated wipe; clear_postgres_chat_sessions.ps1. - Tests: SA repos, pg compat, persist fallback, smoke env isolation; CI and docs touch-ups. Co-authored-by: Cursor <cursoragent@cursor.com>
151 lines
5.9 KiB
Python
151 lines
5.9 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from svc.config.paths import db_path
|
|
from svc.persistence.sqlite_store import SqliteStore
|
|
from svc.persistence.assistant_store import get_assistant_store
|
|
from runtime.chat.tool_invocation_context import (
|
|
current_tool_lane_sessions,
|
|
current_tool_workspace_lane_role,
|
|
)
|
|
from runtime.skill_installer import auto_install_skill_from_payload, install_skill_from_registry_archive
|
|
from runtime.skills_market import get_market_adapter, normalize_skill_market_provider_setting
|
|
from runtime.skills_workspace_lane import resolve_auto_install_parent
|
|
from runtime.tools.base import ToolSpec
|
|
|
|
|
|
def _store() -> SqliteStore:
|
|
return get_assistant_store()
|
|
|
|
|
|
def _coerce_public_flag(raw: Any) -> bool:
|
|
if isinstance(raw, bool):
|
|
return raw
|
|
return str(raw or "").strip().lower() in {"1", "true", "yes", "on", "public"}
|
|
|
|
|
|
def _agent_skill_install_parent(*, public: bool) -> Path:
|
|
role = current_tool_workspace_lane_role()
|
|
owner, sid = current_tool_lane_sessions()
|
|
return resolve_auto_install_parent(
|
|
public=public,
|
|
workspace_lane_role=role,
|
|
workspace_owner_session_id=owner,
|
|
session_id=sid,
|
|
skills_home=None,
|
|
)
|
|
|
|
|
|
def skill_auto_install_tool() -> ToolSpec:
|
|
def _handler(args: dict[str, Any]) -> dict[str, Any]:
|
|
payload = args if isinstance(args, dict) else {}
|
|
store = _store()
|
|
public = _coerce_public_flag(payload.get("public"))
|
|
install_parent = _agent_skill_install_parent(public=public)
|
|
lane_role = current_tool_workspace_lane_role()
|
|
auto_bind = bool(public)
|
|
archive_url = str(payload.get("archive_url") or "").strip()
|
|
slug = str(payload.get("slug") or "").strip()
|
|
version = str(payload.get("version") or "").strip() or None
|
|
overwrite = bool(payload.get("overwrite"))
|
|
provider = normalize_skill_market_provider_setting(
|
|
str(payload.get("provider") or store.get_setting("AIA_SKILL_MARKET_PROVIDER") or "")
|
|
)
|
|
if not archive_url and slug:
|
|
try:
|
|
adapter = get_market_adapter(provider)
|
|
archive_url, _chosen_version = adapter.resolve_archive_url(slug=slug, version=version)
|
|
except Exception:
|
|
archive_url = ""
|
|
if archive_url:
|
|
out = install_skill_from_registry_archive(
|
|
store=store,
|
|
archive_url=archive_url,
|
|
overwrite=overwrite,
|
|
skills_root=install_parent,
|
|
auto_bind=auto_bind,
|
|
)
|
|
return {
|
|
"ok": bool(out.ok),
|
|
"name": out.name,
|
|
"target_dir": out.target_dir,
|
|
"detail": out.detail,
|
|
"error_code": out.error_code,
|
|
"retryable": bool(out.retryable),
|
|
"auto_enabled": bool(getattr(out, "auto_enabled", False)),
|
|
"binding_applied_roles": list(getattr(out, "binding_applied_roles", ()) or []),
|
|
"provider": provider,
|
|
"public": public,
|
|
"install_lane": str(install_parent),
|
|
"workspace_lane_role": lane_role,
|
|
}
|
|
|
|
skill_payload = {
|
|
"name": str(payload.get("name") or "").strip(),
|
|
"description": str(payload.get("description") or "").strip(),
|
|
"body_markdown": str(payload.get("body_markdown") or "").strip(),
|
|
"metadata_oclaw": dict(payload.get("metadata_oclaw") or {})
|
|
if isinstance(payload.get("metadata_oclaw"), dict)
|
|
else {},
|
|
}
|
|
if not skill_payload["name"]:
|
|
return {"ok": False, "error_code": "name_required", "error": "name_required"}
|
|
if not skill_payload["description"]:
|
|
skill_payload["description"] = f"{skill_payload['name']} skill"
|
|
out = auto_install_skill_from_payload(
|
|
store=store,
|
|
payload=skill_payload,
|
|
workspace_install_parent=install_parent,
|
|
auto_bind=auto_bind,
|
|
)
|
|
return {
|
|
"ok": bool(out.ok),
|
|
"name": out.name,
|
|
"target_dir": out.target_dir,
|
|
"detail": out.detail,
|
|
"error_code": out.error_code,
|
|
"retryable": bool(out.retryable),
|
|
"auto_enabled": bool(getattr(out, "auto_enabled", False)),
|
|
"binding_applied_roles": list(getattr(out, "binding_applied_roles", ()) or []),
|
|
"public": public,
|
|
"install_lane": str(install_parent),
|
|
"workspace_lane_role": lane_role,
|
|
}
|
|
|
|
return ToolSpec(
|
|
name="skill_auto_install",
|
|
description=(
|
|
"Auto install a skill under the agent workspace lane. "
|
|
"Use public=true for shared _workspace/public; omit or false for a role folder "
|
|
"sibling to public: _workspace/<skill_binding_role>/ (e.g. generalist, ops)."
|
|
),
|
|
parameters={
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {"type": "string"},
|
|
"description": {"type": "string"},
|
|
"body_markdown": {"type": "string"},
|
|
"metadata_oclaw": {"type": "object"},
|
|
"slug": {"type": "string"},
|
|
"provider": {"type": "string"},
|
|
"version": {"type": "string"},
|
|
"archive_url": {"type": "string"},
|
|
"overwrite": {"type": "boolean"},
|
|
"public": {
|
|
"type": "boolean",
|
|
"description": "If true, install to shared _workspace/public with role auto-bind; if false, install under this tool run's session lane only.",
|
|
},
|
|
},
|
|
"required": [],
|
|
"additionalProperties": False,
|
|
},
|
|
handler=_handler,
|
|
tags=frozenset({"skill", "oclaw", "installer"}),
|
|
risk_level="high",
|
|
timeout_s=120.0,
|
|
)
|
|
|
|
|
|
__all__ = ["skill_auto_install_tool"]
|