@cryptotaxi247 / CoPilot / commits / 344eaa6c

fix: tolerate Velociraptor 0.75.6 orgs() shape + ArtifactsResponse.success bool (#863)

* fix: drop dead ClientConfig schema; tolerate Velociraptor 0.75.6 orgs() shape Velociraptor 0.75.6 changed `SELECT * FROM orgs()` to return `_client_config` as a YAML string instead of a structured dict, which made Pydantic 2 reject the response on agent sync: 1 validation error for VelociraptorOrganizations organizations.0._client_config Input should be a valid dictionary or instance of ClientConfig [type=model_type, input_value='version:\n name: veloci...', input_type=str] The structured `ClientConfig`/`Version`/`Installer`/`LocalBuffer` chain was dead — nothing in CoPilot read `Organization.client_config` or any of its sub-fields. Drop them entirely. Pydantic 2's default `extra='ignore'` then silently drops the unknown `_client_config` key on parse, so any future shape change to that field is also tolerated. pyvelociraptor / gRPC transport layer is unaffected — this is purely a parse-side schema cleanup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: ArtifactsResponse.success typed bool (matches every caller) Same Pydantic 2 strictness regression as the orgs() fix: every callsite of ArtifactsResponse passes success=True/False, but the field was typed str. Pydantic 1 silently coerced bool→"True"; Pydantic 2 rejects with `Input should be a valid string [type=string_type, input_value=True, input_type=bool]`, which 500'd the GET /api/artifacts endpoint. Sibling ArtifactParametersResponse.success was already bool. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: taylor_socfortress <taylor.walton@socfortress.co> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

taylorcopilot committed May 8, 2026 at 17:29 UTC 344eaa6c9b6032838687aa6cf436610b1ff5da19
2 files changed +4 -47
backend/app/agents/velociraptor/schema/agents.py
+3 -46
@@ -56,55 +56,12 @@ class VelociraptorClients(BaseModel):
56 clients: List[VelociraptorClient]
57
58
59 -class Version(BaseModel):
60 - name: str
61 - version: str
62 - commit: str
63 - build_time: str
64 - ci_build_url: str
65 - compiler: str
66 -
67 -
68 -class Installer(BaseModel):
69 - service_name: str
70 - install_path: str
71 - service_description: Optional[str] = None
72 -
73 -
74 -class LocalBuffer(BaseModel):
75 - memory_size: int
76 - disk_size: int
77 - filename_linux: str
78 - filename_windows: str
79 - filename_darwin: str
80 -
81 -
82 -class ClientConfig(BaseModel):
83 - server_urls: List[str]
84 - ca_certificate: str
85 - nonce: str
86 - writeback_darwin: str
87 - writeback_linux: str
88 - writeback_windows: str
89 - tempdir_windows: str
90 - max_poll: int
91 - nanny_max_connection_delay: int
92 - windows_installer: Installer
93 - darwin_installer: Installer
94 - version: Version
95 - use_self_signed_ssl: bool
96 - pinned_server_name: str
97 - max_upload_size: int
98 - local_buffer: LocalBuffer
99 -
100 -
59 class Organization(BaseModel):
60 Name: str
61 OrgId: str
104 - # Pydantic 2 silently drops leading-underscore field annotations as
105 - # PrivateAttr; alias the JSON key so it's actually parsed.
106 - client_config: ClientConfig = Field(alias="_client_config")
107 - model_config = ConfigDict(populate_by_name=True)
62 + # _client_config is intentionally not parsed: Velociraptor 0.75.6 changed
63 + # SELECT * FROM orgs() to return it as a YAML string instead of a structured
64 + # object, and nothing in CoPilot reads it. Pydantic 2 ignores the unknown key.
65
66
67 class VelociraptorOrganizations(BaseModel):
backend/app/connectors/velociraptor/schema/artifacts.py
+1 -1
@@ -36,7 +36,7 @@ class ArtifactsResponse(BaseModel):
36 message: str = Field(...)
37 # make artifacts optional
38 artifacts: Optional[List[Artifacts]] = None
39 - success: str = Field(...)
39 + success: bool = Field(...)
40
41
42 class ArtifactParametersResponse(BaseModel):