Skip to content

Victor agent-service and client API audit

Decision report / proposal only — October 7, 2026 (October 8 UTC). No API
consolidation is implemented by this report. Audited origin/develop
b8e195cc68e009c68b2cfc9b96ac8cf5b74bef84 plus the pending Node/dependency and
activation fixes in fix/vscode-security-locks. A fresh fetch found develop
unchanged. Do not count this document as an accepted FEP or completed milestone.

The implementation tracker owns
current task statuses and restart instructions. The original report below remains a dated
finding/evidence snapshot; its proposed sequence does not supersede the tracker.

Consumer reconciliation — October 8, 2026

Rechecked origin/develop at b19f0f6730a04b52974fae82829c7ed0152b9d01
after #1265. The original report
below is preserved as audit-time evidence, not a list of still-unfixed defects.
Delivery status remains in the canonical tracker; this section records the
consumer relationships and compatibility decisions required by VAS-03b.

The stream request mismatch was repaired in #1252.
Transport termination and cancellation acknowledgement were repaired in
#1262; paused nonstream responses
and their three content consumers were repaired in #1265. Those bounded repairs
are not shared authentication, durable run results, approval/resume UI, remote
cancellation, a release, or C5 acceptance. The API audit's historical RED probes
must not be used to claim those exact VS Code defects still exist.

Consumer / actual call path Present contract and ownership Migration decision / remaining evidence
VS Code chat panel: chatViewProvider.ts → victorClient.ts::streamChat Extension host owns HTTP; compatibility body serves core messages and web message/optional session_id; core [DONE] or web v1 stream_end required Keep the explicit compatibility behavior until both server entry points pass the shared contract. A terminator establishes transport completion only
Composer, terminal suggestions, Smart Paste → VictorClient.chat Core POST /chat; preserves status, run_id, approval_request; one completion guard prevents consuming paused content Preserve #1265 ownership/no-paste regressions. No automatic resume or restart-safe UI pause retention; ordinary transport-error paste fallback remains legacy behavior
Svelte webview: webview-ui/src/stores/chat.ts → VS Code postMessage → ChatViewProvider Embedded webview bridge, not a standalone network client of web/server; host owns credentials and HTTP Share future SDK/state at the host boundary; validate bridge messages separately. Do not invent a second browser REST owner based on the directory name
Active EventBridge: ChatViewProvider._ensureEventBridgeConnected → eventBridgeClient.ts Separate ws connection to /ws/events, subscription/correlation filters; constructor currently sends no authorization Existing-key protected server rejects it. VAS-05g owns the planned credential/connection repair; correlation filtering is not resource authorization
Main VictorClient.connectWebSocket /ws uses first-message API-key auth; optional prefetched session token in URL. No production caller of connectWebSocket() found under vscode-victor/src Inventory as a retained client API, not the active EventBridge transport. Core /session/token is still a placeholder; neither random token nor WS open means authenticated/resumable session
Browser Chainlit: victor/ui/chat_app/app.py Per-browser-session framework VictorClient; calls Python in process, including existing session restore and UI handlers Retain embedded mode. Hosted remote mode needs the shared adapter; session history restore is not durable action reconciliation
Separate web/server/main.py Server entry point with singular-message SSE, X-Session-Id, signed session tokens and bounded in-memory sessions; several IDE endpoints are placeholders No independent browser HTTP client was found in checked-in web/; external consumers remain unknown. Preserve compatibility until usage inventory/parity/deprecation are approved
GraphQL: graphql_schema.py Mounted under the core auth gates when installed; chat resolver calls the orchestrator directly, subscriptions use event infrastructure Retain while auditing external clients; shared authentication does not prove principal/resource policy parity. Delegate to canonical services before claiming equivalent execution semantics
MCP: integrations/mcp/server.py Registry tool execution with an empty context in call_tool; stdio/JSON-RPC shape Keep interoperability; qualify principal/policy context before hosted use. Do not rename it REST or remove it to reduce endpoint count
Public Python protocol adapters: integrations/protocol/{interface,adapters}.py Exported direct/HTTP wrappers; no non-example production consumer imports found elsewhere in the repository Package docstrings claim CLI/VS Code usage not supported by the current call sites. Preserve public imports pending external inventory; VAS-15a covers the reproduced outcome gaps below
Embedded Python / teams / workflows Existing framework client, coordinator and workflow owners No network hop for embedded calls. Shared run envelopes retain formation-specific execution; peer formations do not require a supervisor
Sandhi / InferFlux Gateway/provider and model-serving APIs, independent of Victor's agent-run API Preserve separate token audiences and deployment ownership. Do not make gateway login an approval or expose provider credentials to the UI

Newly isolated legacy adapter gap (G80). Synthetic compatibility probes against
the actual ChatResponse.from_dict convert a paused response with run/approval
fields into {content, tool_calls, finish_reason: "stop", usage} and discard the
pause identity. A real HTTPProtocolAdapter.stream_chat with an httpx.MockTransport
response containing one complete data frame but no terminal marker yields the
partial content and exhausts normally; exactly one POST was observed. These are
local adapter probes, not live server/member/model evidence. Existing protocol
tests do not establish the lost-outcome invariant. Repair in the existing test
owners; do not publish a second result registry or delete the public adapter
because an internal caller was not found.

The existing src/test/eventBridgeClient.test.ts contains placeholder assertions
and sits outside the host runner's test/suite discovery root. It is not current
client authentication evidence; VAS-05g must exercise the real owner in a discovered
suite and remove superseded placeholders only after preserving useful invariants.

Inventory limits. This is checked-in source reachability, not deployed usage
telemetry. External clients, installed extensions and generated/dynamic imports
need explicit compatibility evidence before removal. Existing contract tests are
reused; this documentation increment adds no mirrored parser or inventory-only
runtime test. The review gates and unresolved design choices are in
FEP-0039.

Recommendation

Expose one authoritative Victor agent service to the server web UI, VS Code and
external API consumers. Share request/response schemas, a TypeScript SDK, event
parsing and run-state semantics. Keep UI components and authentication storage
appropriate to their host. Preserve the Python framework and definition SDK;
MCP and any retained GraphQL become thin adapters to the same application services.

Use victor serve as the canonical composition root. Migrate the useful session
store and v1 event work from web/server into framework-owned services and the
existing FastAPI routers. Keep a temporary compatibility entry point for the old
web app. Do not add an HTTP-to-HTTP proxy inside the same deployment merely to
share code. The Sandhi Python binding is required by Victor (the current pin is in
the tech stack);
routing through a separately deployed Sandhi proxy is optional. InferFlux remains
the inference service. Neither owns Victor run approval or state.

Surface inventory

Surface Current purpose/evidence Disposition
Python framework victor/framework/{_api,agent,client,session_config}.py: Agent/TaskResult, VictorClient, StateGraph/workflows/teams Retain; application execution owner behind transport adapters
Definition/plugin SDK victor-contracts/victor_contracts; plugins and vertical definitions Retain independent boundary; do not put HTTP clients or runtime state here
Core FastAPI server victor/integrations/api/fastapi_server.py, routes/: 102 HTTP operations counted in core router factories, excluding separately mounted HITL/GraphQL/plugins Canonical HTTP service; cohesive routers, one composition root
Separate web backend web/server/main.py: separate FastAPI app, sessions, signed tokens, v1 SSE, rendering and placeholder compatibility routes Consolidate useful implementations; deprecate duplicate app after parity
Chainlit web chat victor/ui/chat_app/app.py: per-session VictorClient and approval UI, in process Preserve UI; use remote service adapter for hosted mode, same application service in embedded mode
VS Code HTTP client vscode-victor/src/victorClient.ts: 77 distinct literal HTTP method/path calls; handwritten types and SSE parser Shared generated contract SDK plus thin IDE adapter
Streaming/events /chat/stream, /ws, /ws/events, workflow WS; framework wire events and EventBridge One execution event contract over SSE; keep WS only for demonstrated duplex/subscription needs
Webview bridge Svelte/legacy HTML ↔ extension host postMessage, plus command URI/CodeLens callbacks Validated, discriminated UI messages; network/auth in extension host
GraphQL graphql_schema.py: optional dependency, enabled when available; separate resolvers Freeze expansion; retained resolvers delegate to same services, deprecate only after consumer inventory
MCP integrations/mcp: JSON-RPC tools/resources, including stdio Retain interoperability; align authorization/execution policy, not REST envelope format
Legacy protocol adapters integrations/protocol/{interface,messages,adapters}.py: another DTO family and direct/HTTP adapters Deprecation candidate; no production imports found outside its own package/docs, but external consumers require notice
Inference/provider API Provider adapters, required Sandhi Python binding, optional standalone proxy, InferFlux/OpenAI-compatible models Separate model API from agent-run API; preserve provider abstraction

Counts describe source operations, not successful end-to-end coverage, deployed
routes, or distinct business capabilities. Core API routes include tools, agents,
teams, plans, workflows, configuration, search, LSP, Git, terminal and observability.

Findings and priorities

P0 blocks reliable shared-service exposure; P1 addresses boundary robustness;
P2 is measured simplification/performance. Findings are source observations unless
marked reproduced. Existing tests passing does not negate a missing contract test.

Priority Finding Evidence and user impact Recommended change
P0 Two incompatible chat APIs VS Code streamChat() sends {message, session_id?}; ordinary chat() already sends messages. Streaming core ChatRequest requires messages. Reproduced HTTP 422, missing body.messages, before runtime initialization. The separate web backend accepts the VS Code shape. One versioned chat/run contract and executable cross-language contract fixtures; compatibility adapter for existing callers
P0 Session ownership differs Core caches one _victor_client; separate web uses a bounded per-session in-memory store. Core does not expose the session-header contract expected by VS Code. Web session reuse/token reissuance checks shared API-key access and session existence, not per-principal ownership; an HMAC is not authorization. Explicit principal/workspace/session/run ownership, concurrent-turn policy, durable state where restart is promised
P0 Approval semantics lost in clients REST /chat returns 202 + awaiting_approval, run ID and request; TS chat() discards those fields. /tools/approve uses a separate in-memory map; /chat/resume and /hitl are other surfaces. One approval lifecycle and exact payload/version binding; retain domain adapters, expose resumable run state in both UIs
P0 EOF can appear successful TS stream parser resolves on end without requiring completion; malformed frames are logged then ignored; core and web use different terminators. Bounded UTF-8/SSE parser, explicit terminal run status, interruption/unknown state on premature EOF; stream end alone is not business success
P0 Authentication is inconsistent Core HTTP/WS gates exist and tests pass. EventBridgeClient sends no auth to guarded /ws/events. Core /session/token returns random placeholder IDs; web app signs tokens. VS Code reads server key from ordinary configuration. One authenticated principal resolver, scoped access and protected event subscriptions; host secret storage; remove placeholder security contracts
P0 Cancellation acknowledgement is overstated TS returns true after WS send; core handler has no cancel_tool branch. HTTP returns true client-side for 200 even when body says cancelled:false. Requested/acknowledged/terminal cancellation states; verify runtime/provider effect, preserve already committed effects
P1 Multiple execution seams Core chat uses framework client; completions calls provider directly; GraphQL chat calls orchestrator; MCP executes registry tools with empty context. Route through existing application services with shared policy and attribution; completions need a bounded fast path, not a full agent loop
P1 Duplicated DTOs and weak schema checking Pydantic, protocol dataclasses and handwritten TS overlap; many responses are unknown or cast records. Endpoint test verifies paths only. Publish OpenAPI and event JSON Schema from one authoritative contract owner; generate TS types/client and validate at network boundaries
P1 Capabilities and errors can mislead Empty capabilities implies supported; several catches return empty data on failures. Explicit supported/unavailable/unknown states and typed errors; no silent auth/network-to-empty-success conversion
P1 Webview/Markdown boundaries need tightening Existing CSP is useful; incoming message fields are unchecked; hover trusts combined Markdown broadly. Validate messages and target scope, allowlist trusted command IDs, escape untrusted content, preserve workspace trust and remote path semantics
P2 Parallel event/rendering paths EventBridge has useful bounded queues/drop counters, while chat also streams directly; UI posts cumulative content per chunk. Durable run events authoritative, telemetry separate; sequence/deduplicate events, bounded UI batching and retention after measurement
P2 Inactive command and protocol surface Activation smoke found duplicate symbol IDs and six unregistered advertisements. Local pending dependency branch repairs/prunes them. Keep one command owner; restore dormant features only with real invocation tests; deprecate unused adapters after consumer audit

The September U7 review and UX action plan are useful historical records, but
some assertions are stale: web/server now uses VictorClient, session storage is
typed/bounded, and core HTTP/WS auth gates were repaired. Preserve these changes.
Do not repeat already completed work or claim all historical findings remain open.

Proposed public agent-service contract

Illustrative routes, subject to FEP and compatibility review; not existing API claims.

Resource / operation Contract Customer-visible behavior
GET /v1/capabilities Schema versions, enabled features, limits, auth mode Both UIs show only supported actions and explain incompatibility
Agent definitions Approved single-agent/team/workflow definition and configuration references Expose an agent as a service without arbitrary module loading or client-supplied credentials
POST /v1/sessions Owner, workspace scope and immutable configuration reference Separate tabs/workspaces cannot silently share conversation state
POST /v1/runs Session/definition ID, typed input, deadline/budget and idempotency key Return 202 + stable run ID only after durable acceptance in durable mode
GET /v1/runs/{id} Recorded status, result/artifacts, approvals, errors and timestamps Reconnect/refresh obtains the same authoritative outcome
GET /v1/runs/{id}/events Versioned events with event ID/sequence, run/session/member/action correlation SSE replay from a cursor; explicit retention gaps, no re-execution to recover UI
Approval decision Approval ID, decision, exact payload/version hash, expiry; actor from authenticated context Reject stale/unauthorized decisions before dispatch; continuation uses existing owner
Cancellation request Stable run/action identity; cancellation requested versus completed Stop controls never claim that committed writes were undone
/v1/completions, search/LSP Bounded read-oriented requests with cancellation and feature gates Low latency editor operations without agent orchestration overhead
Health/readiness/admin Separate liveness, capacity/readiness, and privileged configuration Useful diagnostics without exposing credentials or unrestricted administration

Single agents, teams and workflows share run lifecycle/result envelopes, with
typed execution-specific definitions. Do not flatten their semantics or mandate
a supervisor for peer formations. Reuse existing run, pause, journal and team
owners rather than inventing a parallel agent-service state machine.

Shared client architecture and security

Layer Share Keep host-specific
Contracts OpenAPI DTOs, event schemas, compatibility rules, error/state enums No environment-specific credentials
TypeScript SDK Transport interface, auth injection, validated response/event decoder, cancellation, replay cursor Browser fetch versus extension-host fetch/HTTP adapter
State layer Run reducer, event deduplication, pending approval and terminal-state semantics Browser tabs versus VS Code panels/workspaces
UI Optional reusable presentation components if useful VS Code commands, editor APIs, theme/accessibility and file application
Authentication Verified principal, role/scope policy, issuer/audience/expiry checks Browser OIDC session/cookies with CSRF controls; VS Code interactive login + SecretStorage; scoped noninteractive credentials for automation
Deployment One service implementation and route composition Embedded/direct mode versus remote HTTP; optional same-origin UI hosting

OIDC should be the hosted-user posture where configured; a gateway login alone
does not prove Victor endpoint authorization. Retain explicit scoped machine-auth
and tightly constrained local-development modes. Never put reusable bearer
credentials into query strings or webview messages. Browser SSE can use a
same-origin authenticated session or an authenticated fetch stream; native
EventSource cannot attach arbitrary Authorization headers.

Performance decisions and validation

No comparative throughput or latency benchmark was run during this audit. These
are acceptance proposals, not achieved performance claims.

Concern Preferred design Required evidence
Network overhead One UI-to-service hop; pool/reuse connections Compare p50/p95 latency and CPU at ⅛/32 concurrent runs, same model/tasks
Editor responsiveness Bounded completion request, existing cancellation, measured debounce Keystroke-to-suggestion p95, stale-result rate and cancellation latency
Streaming Incremental UTF-8 decoder, full SSE framing, bounded frame/buffer sizes Split every byte boundary, CRLF/multiline, oversized frame, truncated stream and slow consumer tests
UI rendering Deltas plus measured 16–50ms batching as an initial experiment Frame time, first-token visibility and heap growth on long streams; retain only if better
Admission and deadlines Per-workload concurrency/budgets and propagated remaining deadline Saturation, fairness, queue wait and timeout behavior; no arbitrary one-user RPS cap
Durability Persist logical actions, approvals, checkpoints and terminal outcomes; define token-event retention separately Crash after acceptance/commit, reconnect and duplicate requests; no unverified action replay
Resource bounds Preserve bounded session/event queues; partition by owner Slow-client isolation and bounded RSS/task/socket counts after repeated disconnects
Retry policy Retry safe reads/transient transport only within budgets; writes require proven same-key semantics or reconciliation Commit-then-timeout recovery, duplicate-key/different-payload conflict, Retry-After handling

Suggested regression gate: no more than 10% p95 adapter-overhead regression against
a recorded same-workload baseline, plus agreed absolute UX limits after baseline
measurement. No language rewrite, new broker, mandatory Redis, GraphQL expansion
or gRPC migration without a measured requirement. One-process deployments can use
an appropriate SQLite-backed durable store; distributed execution requires a
separately reviewed shared-store/lease/admission design.

PR-sized sequence and adversarial gates

Order Milestone Exit criteria
0 Finish isolated Node/dependency/activation repair Clean audits, real activation/package smoke, reviewed candidate and all CI; no claim of chat-service acceptance
1 FEP + current contract inventory; TDD failing consumer-provider fixtures Reproduce 422, approval-field loss, EOF and false cancellation; track consumer compatibility and deprecation plan
2 Authoritative session/run/auth ownership Cross-principal/workspace denial, concurrent-turn policy, durable acceptance/restart, exact approval binding
3 Canonical FastAPI composition and shared schemas/SDK Port existing web session/wire behavior; both clients use same route/service; legacy adapter parity; no second dispatch
4 Shared event and result lifecycle UTF-8/framing, replay, terminal outcomes, verified cancellation, unknown-write reconciliation and UI reconnect pass
5 UI migration and obsolete-surface removal Web + VS Code against real ephemeral server; both same API; remote workspace, auth expiry, disabled capabilities, accessibility and packaged VSIX smoke
6 Measured performance and shared API release Benchmarks, independent adversarial review, green full CI, released server/SDK/VSIX compatibility
Independent framework path Existing G61/G62/G70 lifecycle fixes, released foundations, then C5 The framework harness need not wait for UI migration or GraphQL deprecation; see VAS-11/12/17/18 in the tracker

Incremental API changes require a FEP under repository rules. Migrate compatibility
adapters with a deprecation window; do not silently change existing public payloads
or remove external interfaces merely because internal references are absent.
Extend existing tests by ownership; replace vacuous/duplicate fixtures only when
their invariant is demonstrably covered. Do not retain both a legacy fake-only
contract suite and an equivalent generated contract suite indefinitely.

Evidence and limits

  • Fresh origin/develop: b8e195cc68e009c68b2cfc9b96ac8cf5b74bef84.
  • Reproduced current TS stream body against the real FastAPI router: HTTP 422,
    missing messages; runtime initialization was not called (no model traffic).
  • Compiled TS client with controlled transport stubs (not live-agent evidence):
    a paused 202 response returns only role/content/toolCalls; a stream containing
    partial content then EOF resolves; HTTP cancelled:false returns true to the UI.
  • Existing route-presence, HTTP/WS auth and resume tests: 23 passed.
  • Existing wire-event, web session-store and web-boundary suites: 33 passed.
  • Pending dependency branch: Node 24.21.0; 999 real-host integration tests, 50
    Vitest tests, zero reported npm vulnerabilities in both graphs, production VSIX
    built. These tests do not prove real-server chat works; this audit found that gap.
  • Existing full Python collection: 33,883 collected; collection is not execution.
  • No provider benchmark, OIDC deployment, public API migration, live mixed-team
    run, release or CI merge was performed as part of this audit.

Standards consulted

Independent adversarial review confirmed the principal findings and operation
count. Corrections incorporated: required Sandhi binding versus optional proxy,
stream-only request-shape mismatch, and HMAC versus session ownership distinction.