// Package agent is the Agent Orchestration bounded context: the agentic // run loop (prompt -> vLLM -> tool calls -> final reply), the chat-run // quota/concurrency guard, and the in-flight/just-finished run registry // that used to live in internal/handlers/chat.go's StreamManager. // // This is a saga coordinator, not a data owner: it holds no SQL table of // its own. It calls billing (usage quota), conversation (persisting // turns) and knowledge (the search_codebase tool) over gRPC, and // workspace (every filesystem/bash tool) over gRPC, exactly replacing // internal/llm/agent.go + internal/llm/tools.go's direct, in-process // calls into internal/db and the local filesystem. // // Rendering (Markdown, syntax highlighting, deliverable cards) stays out // of this service on purpose — see internal/render's doc comment in the // gateway service — so this API deals in plain text and structured tool // results, never HTML. syntax = "proto3"; package enkly.agent.v1; option go_package = "enkly/gen/agent/v1;agentv1"; service AgentService { // StartTurn replaces the synchronous prefix of Handlers.Chat: the // per-user throttle (ChatLimiter), the billing quota check, loading // the conversation, persisting the user's prompt, auto-titling, and // registering a new run (StreamManager.create) before handing off to // a background goroutine. Returns immediately with a stream_id; the // reply itself arrives via StreamTurn. rpc StartTurn(StartTurnRequest) returns (StartTurnResponse); // StreamTurn replaces Handlers.StreamChat: a server-streaming view of // one run's progress. resume_from_tool_index mirrors the monolith's // SSE Last-Event-ID handling, so a reconnecting client doesn't see // tool cards it already has. rpc StreamTurn(StreamTurnRequest) returns (stream TurnEvent); // StopTurn replaces Handlers.StopStream. rpc StopTurn(StopTurnRequest) returns (StopTurnResponse); } enum StartTurnOutcome { START_TURN_OUTCOME_UNSPECIFIED = 0; START_TURN_OUTCOME_OK = 1; START_TURN_OUTCOME_CONVERSATION_NOT_FOUND = 2; START_TURN_OUTCOME_CONVERSATION_BUSY = 3; START_TURN_OUTCOME_TOO_MANY_STREAMS = 4; START_TURN_OUTCOME_RATE_LIMITED = 5; START_TURN_OUTCOME_QUOTA_EXCEEDED = 6; START_TURN_OUTCOME_INTERNAL_ERROR = 7; } // SystemEntry is a mid-conversation instruction/permission/budget // update — see agentrun.SystemEntry (services/agent/internal/ // infrastructure/agentrun/effort.go) for what each kind means and how // it's folded into the system prompt. message SystemEntry { enum Kind { KIND_UNSPECIFIED = 0; KIND_INSTRUCTION = 1; KIND_PERMISSION = 2; KIND_BUDGET = 3; } Kind kind = 1; string content = 2; } message StartTurnRequest { int64 user_id = 1; int64 conversation_id = 2; string prompt = 3; // --- Fields below this line are the TARGET structured API surface // for effort control, fast mode and live system-entry updates — see // pkg/turnopts' package doc comment for why they aren't wired up yet // (this environment has no protoc/go toolchain to regenerate // gen/agent/v1 from this file) and what the working fallback is // today (leading /effort, /fast, /system directive lines parsed out // of `prompt` itself, by pkg/turnopts.Parse — see // application.Service.StartTurn). Once `make proto` is run against // this file, a follow-up change points StartTurn at these fields // directly, with turnopts kept on as the fallback path for any // client that still sends plain-text directives instead. Proto3 // fields are purely additive and backward compatible to add, so // declaring them now costs nothing even before that regeneration — // every field below defaults to its zero value for any client built // against today's compiled stubs. // effort selects one of pkg/effort's five levels ("low", "medium", // "high", "extra", "max") for this turn; empty means "use the // deployment's configured default" (see agentrun.Agent.Effort). string effort = 4; // fast_mode requests fast mode for this turn (see // application.Service.FastAgent) — available on every plan, unlike // dynamic_workflows below. bool fast_mode = 5; // system_entries are folded into the system prompt for this turn // onward (see agentrun.WithSystemEntries) — appended, never // replacing anything, so earlier turns' prefix stays stable. repeated SystemEntry system_entries = 6; } message StartTurnResponse { StartTurnOutcome outcome = 1; string message = 2; // user-facing text for any non-OK outcome string stream_id = 3; // set when outcome == OK } message StreamTurnRequest { int64 user_id = 1; string stream_id = 2; int32 resume_from_tool_index = 3; } message ToolCall { string name = 1; string arguments_json = 2; string result = 3; bool is_error = 4; } // ThinkingRound is one agent round's own reasoning trace, standalone // rather than folded into the running total thinking_delta carries — // the same per-step granularity Claude's interleaved thinking exposes // (one distinct thinking block per tool-use step) rather than one // cumulative blob for the whole turn. round is 0-indexed and matches // the round the agent service's internal run loop was on when this // trace was produced; a client can use it to render reasoning as // discrete, collapsible steps next to the tool call each one preceded, // instead of a single growing "thinking" pane. message ThinkingRound { int32 round = 1; string thinking = 2; } message TurnEvent { oneof event { string thinking_delta = 1; // cumulative reasoning text so far, plain string token_delta = 2; // cumulative reply text so far, plain ToolCall tool_call = 3; // one completed tool call TurnDone done = 4; ThinkingRound thinking_round = 5; // one round's own reasoning trace, standalone } } message TurnDone { string final_text = 1; // plain Markdown source, not yet rendered // every tool call made during the turn, successful or not — the // gateway uses this to build deliverable (download) cards exactly // like internal/handlers/deliverables.go.attachDeliverables. repeated ToolCall tool_calls = 2; } message StopTurnRequest { int64 user_id = 1; string stream_id = 2; } message StopTurnResponse {}