diff --git a/Cargo.lock b/Cargo.lock index dfc9f6234..b7d2b6f3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2273,8 +2273,10 @@ dependencies = [ "fabro-config", "fabro-environment", "fabro-types", + "jsonschema", "lithos-llm", "openapiv3", + "pebble-coding-agent", "prettyplease", "progenitor", "progenitor-client", diff --git a/docs/public/api-reference/fabro-api.yaml b/docs/public/api-reference/fabro-api.yaml index 5241666de..aa06923ed 100644 --- a/docs/public/api-reference/fabro-api.yaml +++ b/docs/public/api-reference/fabro-api.yaml @@ -11227,6 +11227,18 @@ components: agent_control: $ref: "#/components/schemas/AgentControlState" description: Whether the agent is executing normally or waiting for steering after an interrupt. + agent: + oneOf: + - $ref: "#/components/schemas/AgentSessionProjection" + - type: "null" + description: >- + The coding agent's own fold of this stage's `agent.*` and `todo.*` + events, present for agent stages once their first agent event is + stored and absent for every other stage kind. Each stage owns one + fold over its own events, so the lifetime fields are the stage's + totals across every prompt it ran. `activity` reads `running` on + stages recorded before `agent.processing.end` was kept; `state` is + the authority on whether the stage is done. state: $ref: "#/components/schemas/StageState" description: Lifecycle state of the stage projection. @@ -11617,6 +11629,779 @@ components: original_name: type: string + AgentSessionProjection: + description: >- + The coding agent's fold of one stage's event stream: token counts and + provider-reported cost for the root session and each descendant, the + route and where it moved, the context window, tools, MCP servers, + skills, todo lists, subagents, compactions, files touched, and the + prompt in progress. Counts only; pricing a count from the catalog is + fabro's, and lives in `StageProjection.usage`. + type: object + required: + - root_session_id + - route + - activity + - usage + - cost_usd_micros + - messages + - descendants + - context_window + - tools + - mcp_servers + - skills + - subagent_counts + - todos + - subagents + - compactions + - files_touched + - last_file_touched + - prompts + - prompt + properties: + root_session_id: + type: ["string", "null"] + description: The root session, once an event named it. + route: + $ref: "#/components/schemas/AgentSessionRoute" + activity: + $ref: "#/components/schemas/AgentSessionActivity" + usage: + $ref: "#/components/schemas/TokenUsage" + description: The root session's usage over the stage. + cost_usd_micros: + type: ["integer", "null"] + format: uint64 + minimum: 0 + description: The root session's provider-reported cost, when a provider reported one. + messages: + type: integer + format: uint64 + minimum: 0 + description: Committed assistant messages from the root session. + descendants: + type: object + additionalProperties: + $ref: "#/components/schemas/AgentSessionDescendantAccount" + description: Every descendant session's account, by session id. + context_window: + oneOf: + - $ref: "#/components/schemas/ContextWindowSnapshot" + - type: "null" + description: >- + The root session's latest context window. `event_seq` is the + agent's own sequence when it carried one, not the run event seq. + tools: + type: object + additionalProperties: + $ref: "#/components/schemas/AgentSessionToolActivity" + description: Every tool called anywhere in the tree, by the name the model used. + retries: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Model calls retried after a failed attempt, across the tree. + mcp_servers: + type: object + additionalProperties: + $ref: "#/components/schemas/AgentSessionMcpServer" + description: Every MCP server the root configured, by name. + skills: + $ref: "#/components/schemas/AgentSessionSkills" + subagent_counts: + $ref: "#/components/schemas/AgentSessionSubagentCounts" + todos: + type: object + additionalProperties: + $ref: "#/components/schemas/TodoListProjection" + description: >- + Every todo list in the tree, by list id. The root agent's own list + is the one whose id ends with `root_session_id`. + subagents: + type: array + items: + $ref: "#/components/schemas/AgentSessionSubagent" + compactions: + type: array + items: + $ref: "#/components/schemas/AgentSessionCompaction" + description: The root session's compactions, in order. + failovers: + type: array + items: + $ref: "#/components/schemas/AgentSessionRouteFailover" + default: [] + description: Every move the root made to a fallback route, in order. + failover_stopped: + oneOf: + - $ref: "#/components/schemas/AgentSessionFailoverStop" + - type: "null" + description: >- + Why the prompt in progress, or the last one, stayed on its route + and ended there although fallback routes were named. Cleared when + a prompt starts. + files_touched: + type: array + items: + type: string + description: Files written or edited across the tree, sorted. + last_file_touched: + type: ["string", "null"] + prompts: + type: integer + format: uint64 + minimum: 0 + description: How many prompts have started. + prompt: + $ref: "#/components/schemas/AgentSessionPromptDelta" + pending_writes: + type: object + additionalProperties: + type: array + items: + type: string + description: >- + In-flight bookkeeping, not a fact about the session: the paths a + write or edit tool call named, by tool call id, between its start + and its completion. Present only while such a call is open; a + settled projection has no such member. + + AgentSessionActivity: + description: Where a session stands, as its events tell it. + type: string + enum: [idle, running, waiting_for_steer, ended] + + AgentSessionRoute: + description: The route a session runs on, as it reported it. + type: object + required: + - provider + - model + properties: + provider: + type: ["string", "null"] + model: + type: ["string", "null"] + + AgentSessionDescendantAccount: + description: What one descendant session spent, as its own events reported it. + type: object + required: + - parent + - usage + - cost_usd_micros + - messages + - compactions + properties: + parent: + type: string + description: The session that spawned it. + provider: + type: string + description: The provider it runs on, as its `SessionStarted` reported it. + model: + type: string + description: >- + The model it runs on, from its `SessionStarted`; when the start + was not seen, the model of its first answer. + usage: + $ref: "#/components/schemas/TokenUsage" + cost_usd_micros: + type: ["integer", "null"] + format: uint64 + minimum: 0 + messages: + type: integer + format: uint64 + minimum: 0 + description: Committed assistant messages. + compactions: + type: integer + format: uint64 + minimum: 0 + description: Compactions it completed. + + AgentSessionToolActivity: + description: How one tool has been used across the tree. + type: object + required: + - calls + - errors + - open + properties: + calls: + type: integer + format: uint64 + minimum: 0 + description: Calls started. + errors: + type: integer + format: uint64 + minimum: 0 + description: Calls that completed as errors. + open: + type: integer + format: uint64 + minimum: 0 + description: Calls started and not yet completed. + + AgentSessionSubagentCounts: + description: How many child lifecycle events the tree recorded. + type: object + required: + - spawned + - turns_started + - completed + - failed + - closed + properties: + spawned: + type: integer + format: uint64 + minimum: 0 + turns_started: + type: integer + format: uint64 + minimum: 0 + completed: + type: integer + format: uint64 + minimum: 0 + failed: + type: integer + format: uint64 + minimum: 0 + closed: + type: integer + format: uint64 + minimum: 0 + + AgentSessionMcpServer: + description: >- + One MCP server the session configured, and whether it has been called. + `disconnected` set means the server came up and its connection then + closed; otherwise `error` set means it did not start; otherwise it is + ready with `tools`. + type: object + required: + - tools + - error + - invoked + properties: + tools: + type: array + items: + $ref: "#/components/schemas/McpToolSummary" + error: + type: ["string", "null"] + description: Why it did not start, when it did not. + invoked: + type: boolean + description: Whether any of its tools has been called. + disconnected: + type: string + description: What closed its connection during the session, when it closed. + startup_ms: + type: integer + format: uint64 + minimum: 0 + description: >- + Milliseconds from launch to its outcome: to its tools being + listed, or to the failure. Absent until either has been seen. + + AgentSessionSkills: + description: The skills the root session found and the ones activated anywhere in the tree. + type: object + required: + - available + - activated + properties: + available: + type: array + items: + $ref: "#/components/schemas/SkillSummary" + activated: + type: array + items: + $ref: "#/components/schemas/AgentSessionActivatedSkill" + + AgentSessionActivatedSkill: + description: A skill the session activated. + type: object + required: + - name + - source + properties: + name: + type: string + source: + $ref: "#/components/schemas/SkillActivationSource" + + AgentSessionSubagent: + description: One child the root spawned. A reused child stays one row; every event after the spawn moves its status. + type: object + required: + - agent_id + - depth + - task + - status + properties: + agent_id: + type: string + depth: + type: integer + minimum: 0 + task: + type: string + status: + $ref: "#/components/schemas/AgentSessionSubagentStatus" + + AgentSessionSubagentStatus: + description: Where a child stands. + oneOf: + - $ref: "#/components/schemas/AgentSessionSubagentStatusRunning" + - $ref: "#/components/schemas/AgentSessionSubagentStatusCompleted" + - $ref: "#/components/schemas/AgentSessionSubagentStatusFailed" + - $ref: "#/components/schemas/AgentSessionSubagentStatusClosed" + discriminator: + propertyName: status + mapping: + running: "#/components/schemas/AgentSessionSubagentStatusRunning" + completed: "#/components/schemas/AgentSessionSubagentStatusCompleted" + failed: "#/components/schemas/AgentSessionSubagentStatusFailed" + closed: "#/components/schemas/AgentSessionSubagentStatusClosed" + + AgentSessionSubagentStatusRunning: + type: object + required: + - status + properties: + status: + type: string + enum: [running] + + AgentSessionSubagentStatusCompleted: + type: object + required: + - status + - success + - turns_used + properties: + status: + type: string + enum: [completed] + success: + type: boolean + turns_used: + type: integer + minimum: 0 + + AgentSessionSubagentStatusFailed: + type: object + required: + - status + - error + properties: + status: + type: string + enum: [failed] + error: + $ref: "#/components/schemas/AgentErrorData" + + AgentSessionSubagentStatusClosed: + type: object + required: + - status + properties: + status: + type: string + enum: [closed] + + AgentSessionCompaction: + description: One compaction the root session completed. + type: object + required: + - reason + - original_turn_count + - preserved_turn_count + - summary_token_estimate + - tracked_file_count + properties: + reason: + $ref: "#/components/schemas/CompactionReason" + original_turn_count: + type: integer + minimum: 0 + preserved_turn_count: + type: integer + minimum: 0 + summary_token_estimate: + type: integer + minimum: 0 + tracked_file_count: + type: integer + minimum: 0 + usage: + $ref: "#/components/schemas/TokenUsage" + description: >- + The summary call's tokens: a breakdown of the session's and the + prompt's usage, which already include them. Zero on compactions + recorded before it was kept. + cost_usd_micros: + type: integer + format: uint64 + minimum: 0 + description: The summary call's provider-reported cost, included in the totals the same way. + + AgentSessionRouteFailover: + description: One move the root session made to a fallback route, as the stream reported it from the route it moved to. + type: object + required: + - from + - to + - attempt + - error + - usage + - inference_ms + - tool_ms + - continuation + properties: + from: + type: string + description: The `provider/model` that failed. + to: + type: string + description: The `provider/model` the prompt continued on. + attempt: + type: integer + format: uint32 + minimum: 0 + description: How many routes the prompt had moved through, this one included. + error: + $ref: "#/components/schemas/AgentErrorData" + description: The failure that ended the previous route. + usage: + $ref: "#/components/schemas/TokenUsage" + description: >- + What the prompt spent on the failed route. Already in the + session's and the prompt's totals through that route's committed + answers: a breakdown, not an addition. + cost_usd_micros: + type: integer + format: uint64 + minimum: 0 + inference_ms: + type: integer + format: uint64 + minimum: 0 + description: Milliseconds the prompt spent waiting on the failed route's model. + tool_ms: + type: integer + format: uint64 + minimum: 0 + description: Milliseconds the prompt spent running tools on the failed route. + continuation: + $ref: "#/components/schemas/FailoverContinuation" + + AgentSessionFailoverStop: + description: Why a prompt stayed on its route and ended there although fallback routes were named. + type: object + required: + - route + - attempt + - reason + - error + properties: + route: + type: string + description: The `provider/model` the prompt ended on. + attempt: + type: integer + format: uint32 + minimum: 0 + description: How many fallback routes the prompt had moved through; `0` on the route it started on. + reason: + $ref: "#/components/schemas/FailoverStop" + error: + $ref: "#/components/schemas/AgentErrorData" + description: The failure that ended the prompt. + + AgentSessionPromptDelta: + description: >- + What the prompt in progress, or the last one, did: reset when a prompt + starts, complete once `completed` is set. + type: object + required: + - completed + - usage + - cost_usd_micros + - messages + - context_window + - tool_calls + - descendants + - subagents + - compactions + - files_touched + - last_file_touched + properties: + completed: + type: boolean + description: Whether the prompt reached its end. + usage: + $ref: "#/components/schemas/TokenUsage" + description: The root session's usage over the prompt. + cost_usd_micros: + type: ["integer", "null"] + format: uint64 + minimum: 0 + messages: + type: integer + format: uint64 + minimum: 0 + description: Committed assistant messages. + context_window: + oneOf: + - $ref: "#/components/schemas/ContextWindowSnapshot" + - type: "null" + description: The latest context window the prompt reported. + tool_calls: + type: integer + format: uint64 + minimum: 0 + description: Tool calls started, across the tree. + retries: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Model calls retried after a failed attempt, across the tree. + failovers: + type: integer + format: uint32 + minimum: 0 + default: 0 + description: Moves the root made to a fallback route during the prompt. + descendants: + type: object + additionalProperties: + $ref: "#/components/schemas/AgentSessionDescendantAccount" + description: What each descendant spent during the prompt, by session id. + subagents: + $ref: "#/components/schemas/AgentSessionSubagentCounts" + description: Child lifecycle events during the prompt. + compactions: + type: array + items: + $ref: "#/components/schemas/AgentSessionCompaction" + description: Compactions the root completed during the prompt. + files_touched: + type: array + items: + type: string + description: Files written or edited during the prompt, across the tree, sorted. + last_file_touched: + type: ["string", "null"] + + TokenUsage: + description: >- + Token accounting as the coding agent counts it. The five buckets are + disjoint: every token is counted in exactly one, so their plain sum + is the total. A bucket that is absent reads as zero. + type: object + properties: + input: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Prompt tokens that were neither read from nor written to a cache. + output: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Completion tokens that are not reasoning tokens. + reasoning: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Completion tokens spent on reasoning, billed at the output rate. + cache_read: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Prompt tokens served from a provider cache. + cache_write: + type: integer + format: uint64 + minimum: 0 + default: 0 + description: Prompt tokens written into a provider cache. + + McpToolSummary: + description: One tool an MCP server advertised, as the coding agent's registry named it. + type: object + required: + - name + - original_name + properties: + name: + type: string + description: "The name the model calls: `mcp__{server}__{tool}`." + original_name: + type: string + description: The server's own name for the tool. + + CompactionReason: + description: Why a conversation compaction ran. + type: string + enum: [threshold, manual, overflow] + + FailoverContinuation: + description: >- + How a prompt carries on after a failover. `replay_prompt` when nothing + the prompt committed is in the conversation, so the new route is asked + the prompt again; `continue_turn` when the conversation holds output + or tool results this prompt committed, so the new route continues the + turn from where it stood. + type: string + enum: [replay_prompt, continue_turn] + + FailoverStop: + description: >- + Why a model failure ends a prompt on its route when fallback routes + were named. `ineligible` when the failure follows the request, so + another route would fail the same way; `exhausted` when every named + route has been taken. + type: string + enum: [ineligible, exhausted] + + AgentErrorData: + description: >- + A failure as the coding agent's event stream carries it: category, + safe message, retry advice, provider and model context, and the + rendered source chain. Never a raw provider response body. + type: object + required: + - kind + - message + properties: + kind: + $ref: "#/components/schemas/AgentErrorKind" + message: + type: string + description: The whole failure rendered for a person, cause included. + llm_kind: + $ref: "#/components/schemas/LlmErrorKind" + description: The model-layer category, when a model call failed. + retry: + $ref: "#/components/schemas/LlmRetryClassification" + description: Whether repeating the same model call is safe, when a model call failed. + provider: + type: string + description: The provider that produced the failure, when one was selected. + model: + type: string + description: The model that produced the failure, when one was selected. + status: + type: integer + format: uint16 + minimum: 0 + description: The HTTP status, when the failure came from an HTTP response. + provider_code: + type: string + description: The provider's own error code, as reported on the wire. + provider_retry_after_millis: + type: integer + format: uint64 + minimum: 0 + description: The provider's advised wait in milliseconds. + source_chain: + type: array + items: + type: string + description: The text of each cause below `message`, outermost first. + + AgentErrorKind: + description: The stable category of a coding agent failure. + type: string + enum: + - llm + - compaction + - agent + - invalid_input + - session_closed + - invalid_state + - tool_execution + - interrupted + - tool_rounds_exhausted + - task + - event_stream + + LlmErrorKind: + description: >- + The model layer's stable failure category. Known values are + `configuration`, `model_selection`, `authentication`, + `access_denied`, `not_found`, `invalid_request`, `context_length`, + `rate_limit`, `quota_exceeded`, `content_filter`, `server`, + `provider`, `network`, `timeout`, `stream_decode`, `response_decode`, + `resource_limit`, `middleware`, and `cancelled`. A category written + by a newer model layer is carried as its own spelling. + type: string + example: rate_limit + + LlmRetryClassification: + description: Whether repeating the same resolved model call is safe. + oneOf: + - $ref: "#/components/schemas/LlmRetryClassificationNever" + - $ref: "#/components/schemas/LlmRetryClassificationSafe" + - $ref: "#/components/schemas/LlmRetryClassificationAfter" + discriminator: + propertyName: type + mapping: + never: "#/components/schemas/LlmRetryClassificationNever" + safe: "#/components/schemas/LlmRetryClassificationSafe" + after: "#/components/schemas/LlmRetryClassificationAfter" + + LlmRetryClassificationNever: + description: Repeating the call cannot succeed. + type: object + required: + - type + properties: + type: + type: string + enum: [never] + + LlmRetryClassificationSafe: + description: Repeating the call is safe on the caller's own schedule. + type: object + required: + - type + properties: + type: + type: string + enum: [safe] + + LlmRetryClassificationAfter: + description: Repeating the call is safe after the given delay. + type: object + required: + - type + - after_millis + properties: + type: + type: string + enum: [after] + after_millis: + type: integer + format: uint64 + minimum: 0 + description: The delay in milliseconds. + StageModelUsage: description: Provider, model, and request-control metadata recorded for a stage attempt. type: object diff --git a/lib/foundation/fabro-api/Cargo.toml b/lib/foundation/fabro-api/Cargo.toml index ec0ef3535..874532f9d 100644 --- a/lib/foundation/fabro-api/Cargo.toml +++ b/lib/foundation/fabro-api/Cargo.toml @@ -20,6 +20,7 @@ fabro-config = { path = "../fabro-config" } fabro-environment.workspace = true fabro-types = { path = "../fabro-types" } lithos-llm = { workspace = true, features = ["runtime"] } +pebble-coding-agent.workspace = true progenitor-client = "0.13" regress = "0.10" reqwest.workspace = true @@ -38,3 +39,5 @@ syn = "2" [dev-dependencies] fabro-types = { path = "../fabro-types", features = ["test-support"] } +jsonschema.workspace = true +serde_yaml = "0.9" diff --git a/lib/foundation/fabro-api/build.rs b/lib/foundation/fabro-api/build.rs index 6630080f7..d5c207a1c 100644 --- a/lib/foundation/fabro-api/build.rs +++ b/lib/foundation/fabro-api/build.rs @@ -433,6 +433,122 @@ fn main() { "fabro_types::AgentMcpToolSummary", &[], ), + // Pebble's own fold of a stage's agent events, embedded in + // `StageProjection.agent`. Every nested type is pebble's; the schema + // names carry an `AgentSession` prefix where fabro already has a + // schema of the same name for its own projection. + ( + "AgentSessionProjection", + "pebble_coding_agent::projection::SessionProjection", + &[], + ), + ( + "AgentSessionActivity", + "pebble_coding_agent::projection::SessionActivity", + &[], + ), + ( + "AgentSessionRoute", + "pebble_coding_agent::projection::RouteProjection", + &[], + ), + ( + "AgentSessionDescendantAccount", + "pebble_coding_agent::projection::DescendantAccount", + &[], + ), + ( + "AgentSessionToolActivity", + "pebble_coding_agent::projection::ToolActivity", + &[], + ), + ( + "AgentSessionSubagentCounts", + "pebble_coding_agent::projection::SubagentCounts", + &[], + ), + ( + "AgentSessionMcpServer", + "pebble_coding_agent::projection::McpServerProjection", + &[], + ), + ( + "AgentSessionSkills", + "pebble_coding_agent::projection::SkillsProjection", + &[], + ), + ( + "AgentSessionActivatedSkill", + "pebble_coding_agent::projection::ActivatedSkill", + &[], + ), + ( + "AgentSessionSubagent", + "pebble_coding_agent::projection::SubagentProjection", + &[], + ), + ( + "AgentSessionSubagentStatus", + "pebble_coding_agent::projection::SubagentStatus", + &[], + ), + ( + "AgentSessionCompaction", + "pebble_coding_agent::projection::CompactionProjection", + &[], + ), + ( + "AgentSessionRouteFailover", + "pebble_coding_agent::projection::RouteFailoverProjection", + &[], + ), + ( + "AgentSessionFailoverStop", + "pebble_coding_agent::projection::FailoverStopProjection", + &[], + ), + ( + "AgentSessionPromptDelta", + "pebble_coding_agent::projection::PromptDelta", + &[], + ), + ("TokenUsage", "pebble_coding_agent::events::TokenUsage", &[]), + ( + "McpToolSummary", + "pebble_coding_agent::events::McpToolSummary", + &[], + ), + ( + "CompactionReason", + "pebble_coding_agent::events::CompactionReason", + &[], + ), + ( + "FailoverContinuation", + "pebble_coding_agent::events::FailoverContinuation", + &[], + ), + ( + "FailoverStop", + "pebble_coding_agent::events::FailoverStop", + &[], + ), + ( + "AgentErrorData", + "pebble_coding_agent::events::ErrorData", + &[], + ), + ( + "AgentErrorKind", + "pebble_coding_agent::events::ErrorKind", + &[], + ), + ("LlmErrorKind", "lithos_llm::types::ErrorKind", &[]), + ( + "LlmRetryClassification", + "lithos_llm::types::RetryClassification", + &[], + ), ( "McpHttpProtocol", "fabro_types::settings::run::McpHttpProtocol", diff --git a/lib/foundation/fabro-api/src/lib.rs b/lib/foundation/fabro-api/src/lib.rs index 3ac034c73..485c72e20 100644 --- a/lib/foundation/fabro-api/src/lib.rs +++ b/lib/foundation/fabro-api/src/lib.rs @@ -75,12 +75,31 @@ pub mod types { }; pub use lithos_llm::catalog::{ModelHandle, ProviderId}; pub use lithos_llm::types::{ - ContentPart, Cost as CompletionCost, CostSource, Message, ReasoningEffort, ReasoningOutput, - ResponseFormat as CompletionResponseFormat, Role, Speed as BillingSpeed, + ContentPart, Cost as CompletionCost, CostSource, ErrorKind as LlmErrorKind, Message, + ReasoningEffort, ReasoningOutput, ResponseFormat as CompletionResponseFormat, + RetryClassification as LlmRetryClassification, Role, Speed as BillingSpeed, TokenCounts as CompletionUsage, ToolChoice as CompletionToolChoice, ToolDefinition as CompletionToolDefinition, ToolDefinitionKind as CompletionToolDefinitionKind, }; + /// `StageProjection.agent` is the coding agent's own fold of the stage's + /// events; the API reuses pebble's types under the schema names. + pub use pebble_coding_agent::events::{ + CompactionReason, ErrorData as AgentErrorData, ErrorKind as AgentErrorKind, + FailoverContinuation, FailoverStop, McpToolSummary, TokenUsage, + }; + pub use pebble_coding_agent::projection::{ + ActivatedSkill as AgentSessionActivatedSkill, + CompactionProjection as AgentSessionCompaction, + DescendantAccount as AgentSessionDescendantAccount, + FailoverStopProjection as AgentSessionFailoverStop, + McpServerProjection as AgentSessionMcpServer, PromptDelta as AgentSessionPromptDelta, + RouteFailoverProjection as AgentSessionRouteFailover, RouteProjection as AgentSessionRoute, + SessionActivity as AgentSessionActivity, SessionProjection as AgentSessionProjection, + SkillsProjection as AgentSessionSkills, SubagentCounts as AgentSessionSubagentCounts, + SubagentProjection as AgentSessionSubagent, SubagentStatus as AgentSessionSubagentStatus, + ToolActivity as AgentSessionToolActivity, + }; /// A sandbox's status on the API is the sandbox driver's own type. pub use sandbox_driver::{ NetworkPolicy as SandboxNetworkPolicy, Resources as SandboxResources, SandboxId, diff --git a/lib/foundation/fabro-api/tests/agent_session_projection_round_trip.rs b/lib/foundation/fabro-api/tests/agent_session_projection_round_trip.rs new file mode 100644 index 000000000..1c6a5ba87 --- /dev/null +++ b/lib/foundation/fabro-api/tests/agent_session_projection_round_trip.rs @@ -0,0 +1,629 @@ +//! `StageProjection.agent` is pebble's `SessionProjection`, reused verbatim +//! along with every type nested in it. These tests prove the API types are +//! pebble's own and that the OpenAPI schemas describe pebble's serde shape: +//! a populated projection validates against `AgentSessionProjection`, every +//! key it serializes is declared, and every enum variant this build knows is +//! in the spec. A pebble re-pin that adds a field or a variant fails here +//! until the spec is updated. + +use std::any::{TypeId, type_name}; +use std::collections::BTreeMap; +use std::num::NonZeroU32; +use std::time::{Duration, SystemTime}; + +use fabro_api::types::{ + AgentErrorData as ApiAgentErrorData, AgentErrorKind as ApiAgentErrorKind, + AgentSessionActivatedSkill as ApiAgentSessionActivatedSkill, + AgentSessionActivity as ApiAgentSessionActivity, + AgentSessionCompaction as ApiAgentSessionCompaction, + AgentSessionDescendantAccount as ApiAgentSessionDescendantAccount, + AgentSessionFailoverStop as ApiAgentSessionFailoverStop, + AgentSessionMcpServer as ApiAgentSessionMcpServer, + AgentSessionProjection as ApiAgentSessionProjection, + AgentSessionPromptDelta as ApiAgentSessionPromptDelta, + AgentSessionRoute as ApiAgentSessionRoute, + AgentSessionRouteFailover as ApiAgentSessionRouteFailover, + AgentSessionSkills as ApiAgentSessionSkills, AgentSessionSubagent as ApiAgentSessionSubagent, + AgentSessionSubagentCounts as ApiAgentSessionSubagentCounts, + AgentSessionSubagentStatus as ApiAgentSessionSubagentStatus, + AgentSessionToolActivity as ApiAgentSessionToolActivity, + CompactionReason as ApiCompactionReason, FailoverContinuation as ApiFailoverContinuation, + FailoverStop as ApiFailoverStop, LlmErrorKind as ApiLlmErrorKind, + LlmRetryClassification as ApiLlmRetryClassification, McpToolSummary as ApiMcpToolSummary, + StageProjection as ApiStageProjection, TokenUsage as ApiTokenUsage, +}; +use fabro_types::StageProjection; +use lithos_llm::types::{ErrorKind as LlmErrorKind, RetryClassification}; +use pebble_coding_agent::events::{ + CodingAgentEvent, CodingEvent, CompactionReason, ContextWindowCountMethod, + ContextWindowSnapshot, ContextWindowStaleness, ErrorData, ErrorKind, FailoverContinuation, + FailoverStop, InputSource, LlmRetryPhase, McpToolSummary, SkillActivationSource, SkillSummary, + TodoCreatedProps, TodoListKind, TodoStatus, TokenUsage, +}; +use pebble_coding_agent::projection::{ + ActivatedSkill, CompactionProjection, DescendantAccount, FailoverStopProjection, + McpServerProjection, PromptDelta, RouteFailoverProjection, RouteProjection, SessionActivity, + SessionProjection, SkillsProjection, SubagentCounts, SubagentProjection, SubagentStatus, + ToolActivity, +}; +use pebble_coding_agent::tools::ToolOutputMetadata; +use serde_json::{Value, json}; + +#[test] +fn agent_session_projection_reuses_pebbles_types() { + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); + assert_same_type::(); +} + +#[test] +fn a_populated_projection_matches_its_openapi_schema() { + let projection = scripted_projection(); + // The script reached every section of the projection. + assert_eq!(projection.root_session_id.as_deref(), Some("ses_root")); + assert_eq!(projection.activity, SessionActivity::Idle); + assert_eq!(projection.route.model.as_deref(), Some("claude-fable-5")); + assert_eq!(projection.prompts, 1); + assert!(projection.prompt.completed); + assert_eq!(projection.messages, 2); + assert_eq!(projection.retries, 1); + assert_eq!(projection.descendants.len(), 1); + assert_eq!(projection.tools.len(), 2); + assert_eq!(projection.mcp_servers.len(), 2); + assert_eq!(projection.skills.activated.len(), 1); + assert_eq!(projection.todos.len(), 1); + assert_eq!(projection.subagents.len(), 2); + assert_eq!(projection.compactions.len(), 1); + assert_eq!(projection.failovers.len(), 1); + assert!(projection.failover_stopped.is_some()); + assert_eq!(projection.files_touched, ["/workspace/src/lib.rs"]); + assert!(projection.context_window.is_some()); + + let value = serde_json::to_value(&projection).unwrap(); + assert!( + value.get("pending_writes").is_none(), + "a settled projection keeps its bookkeeping off the wire: {value}" + ); + assert_valid("AgentSessionProjection", &value); + assert_declared( + &spec(), + &json!({ "$ref": "#/components/schemas/AgentSessionProjection" }), + &value, + "agent", + ); + + let api: ApiAgentSessionProjection = serde_json::from_value(value.clone()).unwrap(); + assert_eq!(api, projection); + assert_eq!(serde_json::to_value(&api).unwrap(), value); +} + +#[test] +fn a_projection_taken_mid_write_declares_its_open_write() { + let mut projection = scripted_projection(); + projection.apply(&root(CodingEvent::ToolCallStarted { + tool_name: "write_file".to_string(), + tool_call_id: "call_open".to_string(), + arguments: json!({"file_path": "/workspace/README.md", "content": "x"}), + })); + let value = serde_json::to_value(&projection).unwrap(); + assert_eq!( + value["pending_writes"]["call_open"], + json!(["/workspace/README.md"]) + ); + assert_valid("AgentSessionProjection", &value); + assert_declared( + &spec(), + &json!({ "$ref": "#/components/schemas/AgentSessionProjection" }), + &value, + "agent", + ); +} + +#[test] +fn a_stage_projection_carrying_the_fold_matches_its_openapi_schema() { + let mut stage = StageProjection::new(NonZeroU32::new(1).unwrap()); + stage.agent = Some(scripted_projection()); + let value = serde_json::to_value(&stage).unwrap(); + assert!(value["agent"].is_object()); + assert_valid("StageProjection", &value); + assert_declared( + &spec(), + &json!({ "$ref": "#/components/schemas/StageProjection" }), + &value, + "stage", + ); + let api: ApiStageProjection = serde_json::from_value(value.clone()).unwrap(); + assert_eq!(serde_json::to_value(&api).unwrap(), value); + + let without: StageProjection = serde_json::from_value(json!({ + "first_event_seq": 1, + "prompt": null, + "response": null, + "completion": null, + "provider_used": null, + "diff": null, + "script_invocation": null, + "script_timing": null, + "parallel_results": null, + "output": null, + "usage": { + "input_tokens": 0, + "output_tokens": 0, + "total_tokens": 0, + "reasoning_tokens": 0, + "cache_read_tokens": 0, + "cache_write_tokens": 0 + }, + "agent_control": "running", + "state": "running" + })) + .unwrap(); + assert!( + without.agent.is_none(), + "a stage written before the fold existed has none" + ); + assert!( + serde_json::to_value(&without) + .unwrap() + .get("agent") + .is_none(), + "a stage without a fold serializes none" + ); +} + +#[test] +fn every_enum_variant_this_build_knows_is_in_the_spec() { + for activity in [ + SessionActivity::Idle, + SessionActivity::Running, + SessionActivity::WaitingForSteer, + SessionActivity::Ended, + ] { + assert_valid( + "AgentSessionActivity", + &serde_json::to_value(activity).unwrap(), + ); + } + for reason in [ + CompactionReason::Threshold, + CompactionReason::Manual, + CompactionReason::Overflow, + ] { + assert_valid("CompactionReason", &serde_json::to_value(reason).unwrap()); + } + for continuation in [ + FailoverContinuation::ReplayPrompt, + FailoverContinuation::ContinueTurn, + ] { + assert_valid( + "FailoverContinuation", + &serde_json::to_value(continuation).unwrap(), + ); + } + for stop in [FailoverStop::Ineligible, FailoverStop::Exhausted] { + assert_valid("FailoverStop", &serde_json::to_value(stop).unwrap()); + } + for kind in [ + ErrorKind::Llm, + ErrorKind::Compaction, + ErrorKind::Agent, + ErrorKind::InvalidInput, + ErrorKind::SessionClosed, + ErrorKind::InvalidState, + ErrorKind::ToolExecution, + ErrorKind::Interrupted, + ErrorKind::ToolRoundsExhausted, + ErrorKind::Task, + ErrorKind::EventStream, + ] { + assert_valid("AgentErrorKind", &serde_json::to_value(kind).unwrap()); + } + for retry in [ + RetryClassification::Never, + RetryClassification::Safe, + RetryClassification::after(Duration::from_millis(1500)), + ] { + let value = serde_json::to_value(retry).unwrap(); + assert_valid("LlmRetryClassification", &value); + assert_declared( + &spec(), + &json!({ "$ref": "#/components/schemas/LlmRetryClassification" }), + &value, + "retry", + ); + } + for kind in [ + LlmErrorKind::RateLimit, + LlmErrorKind::ContextLength, + LlmErrorKind::Unknown("later".to_string()), + ] { + assert_valid("LlmErrorKind", &serde_json::to_value(kind).unwrap()); + } + for status in [ + SubagentStatus::Running, + SubagentStatus::Completed { + success: true, + turns_used: 3, + }, + SubagentStatus::Failed { error: llm_error() }, + SubagentStatus::Closed, + ] { + let value = serde_json::to_value(&status).unwrap(); + assert_valid("AgentSessionSubagentStatus", &value); + assert_declared( + &spec(), + &json!({ "$ref": "#/components/schemas/AgentSessionSubagentStatus" }), + &value, + "status", + ); + } +} + +// --- The scripted session ------------------------------------------------- + +fn scripted_projection() -> SessionProjection { + let mut projection = SessionProjection::new(); + projection.apply_all(&scripted_events()); + projection +} + +/// One prompt that touches every section: MCP servers up and down, skills, +/// a todo, a completed and a failed child, a retry, a failover with a +/// compaction on the new route, and a stop. +fn scripted_events() -> Vec { + vec![ + root(CodingEvent::SessionStarted { + provider: Some("openai".to_string()), + model: Some("gpt-5.2".to_string()), + }), + root(CodingEvent::McpServerReady { + server: "github".to_string(), + tools: vec![McpToolSummary { + name: "mcp__github__list_issues".to_string(), + original_name: "list_issues".to_string(), + }], + startup_ms: 842, + }), + root(CodingEvent::McpServerFailed { + server: "broken".to_string(), + error: "could not launch".to_string(), + startup_ms: 3, + }), + root(CodingEvent::SkillsDiscovered { + profile: "anthropic".to_string(), + source_dirs: Vec::new(), + skills: vec![SkillSummary { + name: "rust".to_string(), + description: "Rust workflow help".to_string(), + }], + skipped: Vec::new(), + }), + root(CodingEvent::UserInput { + text: "build it".to_string(), + content: None, + source: InputSource::Prompt, + }), + root(message("openai", "gpt-5.2", 100, 10, Some(500))), + root(CodingEvent::ToolCallStarted { + tool_name: "mcp__github__list_issues".to_string(), + tool_call_id: "call_1".to_string(), + arguments: json!({}), + }), + root(completed("mcp__github__list_issues", "call_1", false)), + root(CodingEvent::ToolCallStarted { + tool_name: "write_file".to_string(), + tool_call_id: "call_2".to_string(), + arguments: json!({"file_path": "/workspace/src/lib.rs", "content": "pub fn x() {}"}), + }), + root(completed("write_file", "call_2", false)), + root(CodingEvent::SkillActivated { + skill_name: "rust".to_string(), + source: SkillActivationSource::Tool, + }), + root(CodingEvent::TodoCreated(TodoCreatedProps { + list_id: TodoListKind::AnthropicTasks.list_id("ses_root"), + list_kind: TodoListKind::AnthropicTasks, + todo_id: "t1".to_string(), + status: TodoStatus::InProgress, + order: 0, + subject: "write tests".to_string(), + description: String::new(), + active_form: Some("Writing tests".to_string()), + owner: None, + blocks: Vec::new(), + blocked_by: Vec::new(), + metadata: BTreeMap::new(), + })), + root(CodingEvent::SubAgentSpawned { + agent_id: "sub-1".to_string(), + depth: 1, + task: "review".to_string(), + generation: 1, + }), + child(CodingEvent::SessionStarted { + provider: Some("openai".to_string()), + model: Some("gpt-5.2-mini".to_string()), + }), + child(message("openai", "gpt-5.2-mini", 7, 1, None)), + root(CodingEvent::SubAgentCompleted { + agent_id: "sub-1".to_string(), + depth: 1, + generation: 1, + success: true, + turns_used: 1, + }), + root(CodingEvent::SubAgentSpawned { + agent_id: "sub-2".to_string(), + depth: 1, + task: "check the tests".to_string(), + generation: 1, + }), + root(CodingEvent::SubAgentFailed { + agent_id: "sub-2".to_string(), + depth: 1, + generation: 1, + error: ErrorData::new(ErrorKind::Agent, "boom"), + }), + root(CodingEvent::LlmRetry { + provider: "openai".to_string(), + model: "gpt-5.2".to_string(), + attempt: 0, + delay_secs: 0.1, + error: ErrorData::new(ErrorKind::Llm, "slow down"), + phase: LlmRetryPhase::Open, + }), + root(CodingEvent::RouteFailover { + from: "openai/gpt-5.2".to_string(), + to: "anthropic/claude-fable-5".to_string(), + attempt: 1, + error: llm_error(), + usage: TokenUsage { + input: 100, + output: 10, + ..TokenUsage::default() + }, + cost_usd_micros: Some(500), + inference_ms: 120, + tool_ms: 30, + continuation: FailoverContinuation::ContinueTurn, + }), + root(CodingEvent::CompactionCompleted { + original_turn_count: 20, + preserved_turn_count: 6, + summary_token_estimate: 500, + tracked_file_count: 1, + reason: CompactionReason::Threshold, + usage: TokenUsage { + input: 30, + ..TokenUsage::default() + }, + cost_usd_micros: Some(2), + }), + root(message("anthropic", "claude-fable-5", 50, 5, Some(300))), + root(CodingEvent::RouteFailoverStopped { + route: "anthropic/claude-fable-5".to_string(), + attempt: 1, + reason: FailoverStop::Exhausted, + error: llm_error(), + }), + root(CodingEvent::ProcessingEnd), + ] +} + +fn root(event: CodingEvent) -> CodingAgentEvent { + CodingAgentEvent::new("ses_root".to_string(), event, SystemTime::UNIX_EPOCH) +} + +fn child(event: CodingEvent) -> CodingAgentEvent { + CodingAgentEvent::new("ses_child".to_string(), event, SystemTime::UNIX_EPOCH) + .with_parent_session_id("ses_root".to_string()) +} + +fn message(provider: &str, model: &str, input: u64, output: u64, cost: Option) -> CodingEvent { + CodingEvent::AssistantMessage { + text: "ok".to_string(), + model: model.to_string(), + usage: TokenUsage { + input, + output, + ..TokenUsage::default() + }, + cost_usd_micros: cost, + cost_source: None, + tool_call_count: 0, + context_window: Some(ContextWindowSnapshot { + provider: provider.to_string(), + model: model.to_string(), + context_window_tokens: 400_000, + input_tokens: 123_456, + usage_percent: 30.864, + count_method: ContextWindowCountMethod::LocalEstimate, + staleness: ContextWindowStaleness::Live, + generated_at: SystemTime::UNIX_EPOCH, + event_seq: Some(9), + breakdown: Vec::new(), + warnings: Vec::new(), + }), + reasoning: None, + } +} + +fn completed(tool_name: &str, tool_call_id: &str, is_error: bool) -> CodingEvent { + CodingEvent::ToolCallCompleted { + tool_name: tool_name.to_string(), + tool_call_id: tool_call_id.to_string(), + output: json!("done"), + metadata: ToolOutputMetadata::default(), + is_error, + error_kind: None, + output_bytes_observed: 4, + output_bytes_retained: 4, + output_bytes_omitted: 0, + } +} + +/// A model-layer failure with every optional member set. +fn llm_error() -> ErrorData { + let mut error = ErrorData::new(ErrorKind::Llm, "rate limited: 429 Too Many Requests") + .with_provider("openai") + .with_model("gpt-5.2"); + error.llm_kind = Some(LlmErrorKind::RateLimit); + error.retry = Some(RetryClassification::after(Duration::from_millis(1500))); + error.status = Some(429); + error.provider_code = Some("rate_limit_exceeded".to_string()); + error.provider_retry_after_millis = Some(1500); + error.source_chain = vec!["429 Too Many Requests".to_string()]; + error +} + +// --- The spec as a JSON schema -------------------------------------------- + +#[expect( + clippy::disallowed_methods, + reason = "a synchronous test reads the spec from the repository once" +)] +fn spec() -> Value { + let text = std::fs::read_to_string(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../../docs/public/api-reference/fabro-api.yaml" + )) + .expect("the OpenAPI spec is in the repository"); + let yaml: serde_yaml::Value = serde_yaml::from_str(&text).expect("the spec parses"); + serde_json::to_value(yaml).expect("the spec is JSON-compatible") +} + +/// Validates `value` against one component schema, with the whole document +/// as the root so `$ref`s resolve. +fn assert_valid(schema_name: &str, value: &Value) { + let mut root = spec(); + root["$ref"] = json!(format!("#/components/schemas/{schema_name}")); + let validator = jsonschema::validator_for(&root).expect("the spec compiles as a JSON schema"); + let errors: Vec = validator + .iter_errors(value) + .map(|error| format!("{error} at {}", error.instance_path())) + .collect(); + assert!( + errors.is_empty(), + "{schema_name} rejects {value:#}:\n{}", + errors.join("\n") + ); +} + +fn resolve<'a>(spec: &'a Value, schema: &'a Value) -> &'a Value { + match schema.get("$ref").and_then(Value::as_str) { + Some(reference) => resolve( + spec, + spec.pointer(reference.trim_start_matches('#')) + .unwrap_or_else(|| panic!("unresolved {reference}")), + ), + None => schema, + } +} + +/// Every key `value` serializes is a declared property of `schema`, and +/// every required property is present, recursively. This is what catches a +/// pebble field the spec does not know yet, since the schemas do not forbid +/// additional properties. +fn assert_declared(spec: &Value, schema: &Value, value: &Value, path: &str) { + let schema = resolve(spec, schema); + if let Some(variants) = schema.get("oneOf").and_then(Value::as_array) { + if value.is_null() { + return; + } + let chosen = match schema.get("discriminator") { + Some(discriminator) => { + let property = discriminator["propertyName"] + .as_str() + .expect("a discriminator names its property"); + let tag = value[property] + .as_str() + .unwrap_or_else(|| panic!("{path}: no `{property}` tag in {value}")); + let target = discriminator["mapping"][tag] + .as_str() + .unwrap_or_else(|| panic!("{path}: `{tag}` is not a mapped variant")); + spec.pointer(target.trim_start_matches('#')) + .unwrap_or_else(|| panic!("unresolved {target}")) + } + None => variants + .iter() + .map(|variant| resolve(spec, variant)) + .find(|variant| variant.get("type").and_then(Value::as_str) != Some("null")) + .unwrap_or_else(|| panic!("{path}: no non-null variant")), + }; + assert_declared(spec, chosen, value, path); + return; + } + match value { + Value::Object(object) => { + if let Some(additional) = schema.get("additionalProperties") { + if additional.is_object() { + for (key, member) in object { + assert_declared(spec, additional, member, &format!("{path}.{key}")); + } + } + return; + } + let properties = schema + .get("properties") + .and_then(Value::as_object) + .unwrap_or_else(|| panic!("{path}: the schema declares no properties")); + for (key, member) in object { + let property = properties + .get(key) + .unwrap_or_else(|| panic!("{path}.{key} is serialized but not declared")); + assert_declared(spec, property, member, &format!("{path}.{key}")); + } + for required in schema + .get("required") + .and_then(Value::as_array) + .into_iter() + .flatten() + { + let key = required.as_str().expect("required names are strings"); + assert!( + object.contains_key(key), + "{path}.{key} is required but not serialized" + ); + } + } + Value::Array(items) => { + if let Some(item_schema) = schema.get("items") { + for (index, item) in items.iter().enumerate() { + assert_declared(spec, item_schema, item, &format!("{path}[{index}]")); + } + } + } + Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {} + } +} + +fn assert_same_type() { + assert_eq!( + TypeId::of::(), + TypeId::of::(), + "{} should be the same type as {}", + type_name::(), + type_name::() + ); +} diff --git a/lib/packages/fabro-api-client/src/.openapi-generator/FILES b/lib/packages/fabro-api-client/src/.openapi-generator/FILES index 88e1306a8..1c312fcf2 100644 --- a/lib/packages/fabro-api-client/src/.openapi-generator/FILES +++ b/lib/packages/fabro-api-client/src/.openapi-generator/FILES @@ -29,9 +29,30 @@ configuration.ts index.ts models/activated-skill.ts models/agent-control-state.ts +models/agent-error-data.ts +models/agent-error-kind.ts models/agent-event-props.ts models/agent-mcp-tool-summary.ts models/agent-session-activated-props.ts +models/agent-session-activated-skill.ts +models/agent-session-activity.ts +models/agent-session-compaction.ts +models/agent-session-descendant-account.ts +models/agent-session-failover-stop.ts +models/agent-session-mcp-server.ts +models/agent-session-projection.ts +models/agent-session-prompt-delta.ts +models/agent-session-route-failover.ts +models/agent-session-route.ts +models/agent-session-skills.ts +models/agent-session-subagent-counts.ts +models/agent-session-subagent-status-closed.ts +models/agent-session-subagent-status-completed.ts +models/agent-session-subagent-status-failed.ts +models/agent-session-subagent-status-running.ts +models/agent-session-subagent-status.ts +models/agent-session-subagent.ts +models/agent-session-tool-activity.ts models/agent-tools-available-props.ts models/aggregate-billing-totals.ts models/aggregate-billing.ts @@ -80,6 +101,7 @@ models/close-run-pull-request-response.ts models/code-location.ts models/command-log-response.ts models/command-termination.ts +models/compaction-reason.ts models/completion-content-part.ts models/completion-cost.ts models/completion-message.ts @@ -147,6 +169,8 @@ models/exec-output-tail.ts models/execute-query-request.ts models/execute-query-response-rows-inner-inner.ts models/execute-query-response.ts +models/failover-continuation.ts +models/failover-stop.ts models/failure-category.ts models/failure-detail.ts models/failure-reason.ts @@ -203,6 +227,10 @@ models/interview-provider-settings.ts models/interview-question-record.ts models/link-run-pull-request-request.ts models/llm-output-kind.ts +models/llm-retry-classification-after.ts +models/llm-retry-classification-never.ts +models/llm-retry-classification-safe.ts +models/llm-retry-classification.ts models/log-destination.ts models/manifest-args.ts models/manifest-config.ts @@ -222,6 +250,7 @@ models/mcp-server-status-failed.ts models/mcp-server-status-ready.ts models/mcp-server-status.ts models/mcp-server.ts +models/mcp-tool-summary.ts models/mcp-transport-http.ts models/mcp-transport-sandbox.ts models/mcp-transport-stdio.ts @@ -531,6 +560,7 @@ models/todo-list-kind.ts models/todo-list-projection.ts models/todo-projection.ts models/todo-status.ts +models/token-usage.ts models/tool-category.ts models/tool-source-application.ts models/tool-source-mcp.ts diff --git a/lib/packages/fabro-api-client/src/models/agent-error-data.ts b/lib/packages/fabro-api-client/src/models/agent-error-data.ts new file mode 100644 index 000000000..3172dd416 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-error-data.ts @@ -0,0 +1,64 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentErrorKind } from './agent-error-kind'; +// May contain unused imports in some cases +// @ts-ignore +import type { LlmRetryClassification } from './llm-retry-classification'; + +/** + * A failure as the coding agent\'s event stream carries it: category, safe message, retry advice, provider and model context, and the rendered source chain. Never a raw provider response body. + */ +export interface AgentErrorData { + 'kind': AgentErrorKind; + /** + * The whole failure rendered for a person, cause included. + */ + 'message': string; + /** + * The model-layer category, when a model call failed. + */ + 'llm_kind'?: string; + /** + * Whether repeating the same model call is safe, when a model call failed. + */ + 'retry'?: LlmRetryClassification; + /** + * The provider that produced the failure, when one was selected. + */ + 'provider'?: string; + /** + * The model that produced the failure, when one was selected. + */ + 'model'?: string; + /** + * The HTTP status, when the failure came from an HTTP response. + */ + 'status'?: number; + /** + * The provider\'s own error code, as reported on the wire. + */ + 'provider_code'?: string; + /** + * The provider\'s advised wait in milliseconds. + */ + 'provider_retry_after_millis'?: number; + /** + * The text of each cause below `message`, outermost first. + */ + 'source_chain'?: Array; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-error-kind.ts b/lib/packages/fabro-api-client/src/models/agent-error-kind.ts new file mode 100644 index 000000000..771f9d41b --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-error-kind.ts @@ -0,0 +1,35 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * The stable category of a coding agent failure. + */ + +export const AgentErrorKind = { + LLM: 'llm', + COMPACTION: 'compaction', + AGENT: 'agent', + INVALID_INPUT: 'invalid_input', + SESSION_CLOSED: 'session_closed', + INVALID_STATE: 'invalid_state', + TOOL_EXECUTION: 'tool_execution', + INTERRUPTED: 'interrupted', + TOOL_ROUNDS_EXHAUSTED: 'tool_rounds_exhausted', + TASK: 'task', + EVENT_STREAM: 'event_stream' +} as const; + +export type AgentErrorKind = typeof AgentErrorKind[keyof typeof AgentErrorKind]; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-activated-skill.ts b/lib/packages/fabro-api-client/src/models/agent-session-activated-skill.ts new file mode 100644 index 000000000..f98711cec --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-activated-skill.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { SkillActivationSource } from './skill-activation-source'; + +/** + * A skill the session activated. + */ +export interface AgentSessionActivatedSkill { + 'name': string; + 'source': SkillActivationSource; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-activity.ts b/lib/packages/fabro-api-client/src/models/agent-session-activity.ts new file mode 100644 index 000000000..ddbe4363f --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-activity.ts @@ -0,0 +1,28 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Where a session stands, as its events tell it. + */ + +export const AgentSessionActivity = { + IDLE: 'idle', + RUNNING: 'running', + WAITING_FOR_STEER: 'waiting_for_steer', + ENDED: 'ended' +} as const; + +export type AgentSessionActivity = typeof AgentSessionActivity[keyof typeof AgentSessionActivity]; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-compaction.ts b/lib/packages/fabro-api-client/src/models/agent-session-compaction.ts new file mode 100644 index 000000000..e1c74598d --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-compaction.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { CompactionReason } from './compaction-reason'; +// May contain unused imports in some cases +// @ts-ignore +import type { TokenUsage } from './token-usage'; + +/** + * One compaction the root session completed. + */ +export interface AgentSessionCompaction { + 'reason': CompactionReason; + 'original_turn_count': number; + 'preserved_turn_count': number; + 'summary_token_estimate': number; + 'tracked_file_count': number; + /** + * The summary call\'s tokens: a breakdown of the session\'s and the prompt\'s usage, which already include them. Zero on compactions recorded before it was kept. + */ + 'usage'?: TokenUsage; + /** + * The summary call\'s provider-reported cost, included in the totals the same way. + */ + 'cost_usd_micros'?: number; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-descendant-account.ts b/lib/packages/fabro-api-client/src/models/agent-session-descendant-account.ts new file mode 100644 index 000000000..8b21d712c --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-descendant-account.ts @@ -0,0 +1,46 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { TokenUsage } from './token-usage'; + +/** + * What one descendant session spent, as its own events reported it. + */ +export interface AgentSessionDescendantAccount { + /** + * The session that spawned it. + */ + 'parent': string; + /** + * The provider it runs on, as its `SessionStarted` reported it. + */ + 'provider'?: string; + /** + * The model it runs on, from its `SessionStarted`; when the start was not seen, the model of its first answer. + */ + 'model'?: string; + 'usage': TokenUsage; + 'cost_usd_micros': number | null; + /** + * Committed assistant messages. + */ + 'messages': number; + /** + * Compactions it completed. + */ + 'compactions': number; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-failover-stop.ts b/lib/packages/fabro-api-client/src/models/agent-session-failover-stop.ts new file mode 100644 index 000000000..90ae80cd4 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-failover-stop.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentErrorData } from './agent-error-data'; +// May contain unused imports in some cases +// @ts-ignore +import type { FailoverStop } from './failover-stop'; + +/** + * Why a prompt stayed on its route and ended there although fallback routes were named. + */ +export interface AgentSessionFailoverStop { + /** + * The `provider/model` the prompt ended on. + */ + 'route': string; + /** + * How many fallback routes the prompt had moved through; `0` on the route it started on. + */ + 'attempt': number; + 'reason': FailoverStop; + /** + * The failure that ended the prompt. + */ + 'error': AgentErrorData; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-mcp-server.ts b/lib/packages/fabro-api-client/src/models/agent-session-mcp-server.ts new file mode 100644 index 000000000..5e6d91bed --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-mcp-server.ts @@ -0,0 +1,41 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { McpToolSummary } from './mcp-tool-summary'; + +/** + * One MCP server the session configured, and whether it has been called. `disconnected` set means the server came up and its connection then closed; otherwise `error` set means it did not start; otherwise it is ready with `tools`. + */ +export interface AgentSessionMcpServer { + 'tools': Array; + /** + * Why it did not start, when it did not. + */ + 'error': string | null; + /** + * Whether any of its tools has been called. + */ + 'invoked': boolean; + /** + * What closed its connection during the session, when it closed. + */ + 'disconnected'?: string; + /** + * Milliseconds from launch to its outcome: to its tools being listed, or to the failure. Absent until either has been seen. + */ + 'startup_ms'?: number; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-projection.ts b/lib/packages/fabro-api-client/src/models/agent-session-projection.ts new file mode 100644 index 000000000..2ec0ff6e7 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-projection.ts @@ -0,0 +1,131 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionActivity } from './agent-session-activity'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionCompaction } from './agent-session-compaction'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionDescendantAccount } from './agent-session-descendant-account'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionFailoverStop } from './agent-session-failover-stop'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionMcpServer } from './agent-session-mcp-server'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionPromptDelta } from './agent-session-prompt-delta'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionRoute } from './agent-session-route'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionRouteFailover } from './agent-session-route-failover'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSkills } from './agent-session-skills'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagent } from './agent-session-subagent'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentCounts } from './agent-session-subagent-counts'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionToolActivity } from './agent-session-tool-activity'; +// May contain unused imports in some cases +// @ts-ignore +import type { ContextWindowSnapshot } from './context-window-snapshot'; +// May contain unused imports in some cases +// @ts-ignore +import type { TodoListProjection } from './todo-list-projection'; +// May contain unused imports in some cases +// @ts-ignore +import type { TokenUsage } from './token-usage'; + +/** + * The coding agent\'s fold of one stage\'s event stream: token counts and provider-reported cost for the root session and each descendant, the route and where it moved, the context window, tools, MCP servers, skills, todo lists, subagents, compactions, files touched, and the prompt in progress. Counts only; pricing a count from the catalog is fabro\'s, and lives in `StageProjection.usage`. + */ +export interface AgentSessionProjection { + /** + * The root session, once an event named it. + */ + 'root_session_id': string | null; + 'route': AgentSessionRoute; + 'activity': AgentSessionActivity; + /** + * The root session\'s usage over the stage. + */ + 'usage': TokenUsage; + /** + * The root session\'s provider-reported cost, when a provider reported one. + */ + 'cost_usd_micros': number | null; + /** + * Committed assistant messages from the root session. + */ + 'messages': number; + /** + * Every descendant session\'s account, by session id. + */ + 'descendants': { [key: string]: AgentSessionDescendantAccount; }; + 'context_window': ContextWindowSnapshot | null; + /** + * Every tool called anywhere in the tree, by the name the model used. + */ + 'tools': { [key: string]: AgentSessionToolActivity; }; + /** + * Model calls retried after a failed attempt, across the tree. + */ + 'retries'?: number; + /** + * Every MCP server the root configured, by name. + */ + 'mcp_servers': { [key: string]: AgentSessionMcpServer; }; + 'skills': AgentSessionSkills; + 'subagent_counts': AgentSessionSubagentCounts; + /** + * Every todo list in the tree, by list id. The root agent\'s own list is the one whose id ends with `root_session_id`. + */ + 'todos': { [key: string]: TodoListProjection; }; + 'subagents': Array; + /** + * The root session\'s compactions, in order. + */ + 'compactions': Array; + /** + * Every move the root made to a fallback route, in order. + */ + 'failovers'?: Array; + 'failover_stopped'?: AgentSessionFailoverStop | null; + /** + * Files written or edited across the tree, sorted. + */ + 'files_touched': Array; + 'last_file_touched': string | null; + /** + * How many prompts have started. + */ + 'prompts': number; + 'prompt': AgentSessionPromptDelta; + /** + * In-flight bookkeeping, not a fact about the session: the paths a write or edit tool call named, by tool call id, between its start and its completion. Present only while such a call is open; a settled projection has no such member. + */ + 'pending_writes'?: { [key: string]: Array; }; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-prompt-delta.ts b/lib/packages/fabro-api-client/src/models/agent-session-prompt-delta.ts new file mode 100644 index 000000000..ce0467016 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-prompt-delta.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionCompaction } from './agent-session-compaction'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionDescendantAccount } from './agent-session-descendant-account'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentCounts } from './agent-session-subagent-counts'; +// May contain unused imports in some cases +// @ts-ignore +import type { ContextWindowSnapshot } from './context-window-snapshot'; +// May contain unused imports in some cases +// @ts-ignore +import type { TokenUsage } from './token-usage'; + +/** + * What the prompt in progress, or the last one, did: reset when a prompt starts, complete once `completed` is set. + */ +export interface AgentSessionPromptDelta { + /** + * Whether the prompt reached its end. + */ + 'completed': boolean; + /** + * The root session\'s usage over the prompt. + */ + 'usage': TokenUsage; + 'cost_usd_micros': number | null; + /** + * Committed assistant messages. + */ + 'messages': number; + 'context_window': ContextWindowSnapshot | null; + /** + * Tool calls started, across the tree. + */ + 'tool_calls': number; + /** + * Model calls retried after a failed attempt, across the tree. + */ + 'retries'?: number; + /** + * Moves the root made to a fallback route during the prompt. + */ + 'failovers'?: number; + /** + * What each descendant spent during the prompt, by session id. + */ + 'descendants': { [key: string]: AgentSessionDescendantAccount; }; + /** + * Child lifecycle events during the prompt. + */ + 'subagents': AgentSessionSubagentCounts; + /** + * Compactions the root completed during the prompt. + */ + 'compactions': Array; + /** + * Files written or edited during the prompt, across the tree, sorted. + */ + 'files_touched': Array; + 'last_file_touched': string | null; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-route-failover.ts b/lib/packages/fabro-api-client/src/models/agent-session-route-failover.ts new file mode 100644 index 000000000..31c868f7d --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-route-failover.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentErrorData } from './agent-error-data'; +// May contain unused imports in some cases +// @ts-ignore +import type { FailoverContinuation } from './failover-continuation'; +// May contain unused imports in some cases +// @ts-ignore +import type { TokenUsage } from './token-usage'; + +/** + * One move the root session made to a fallback route, as the stream reported it from the route it moved to. + */ +export interface AgentSessionRouteFailover { + /** + * The `provider/model` that failed. + */ + 'from': string; + /** + * The `provider/model` the prompt continued on. + */ + 'to': string; + /** + * How many routes the prompt had moved through, this one included. + */ + 'attempt': number; + /** + * The failure that ended the previous route. + */ + 'error': AgentErrorData; + /** + * What the prompt spent on the failed route. Already in the session\'s and the prompt\'s totals through that route\'s committed answers: a breakdown, not an addition. + */ + 'usage': TokenUsage; + 'cost_usd_micros'?: number; + /** + * Milliseconds the prompt spent waiting on the failed route\'s model. + */ + 'inference_ms': number; + /** + * Milliseconds the prompt spent running tools on the failed route. + */ + 'tool_ms': number; + 'continuation': FailoverContinuation; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-route.ts b/lib/packages/fabro-api-client/src/models/agent-session-route.ts new file mode 100644 index 000000000..6b1ae3d7d --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-route.ts @@ -0,0 +1,23 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * The route a session runs on, as it reported it. + */ +export interface AgentSessionRoute { + 'provider': string | null; + 'model': string | null; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-skills.ts b/lib/packages/fabro-api-client/src/models/agent-session-skills.ts new file mode 100644 index 000000000..42fb6d875 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-skills.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionActivatedSkill } from './agent-session-activated-skill'; +// May contain unused imports in some cases +// @ts-ignore +import type { SkillSummary } from './skill-summary'; + +/** + * The skills the root session found and the ones activated anywhere in the tree. + */ +export interface AgentSessionSkills { + 'available': Array; + 'activated': Array; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent-counts.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent-counts.ts new file mode 100644 index 000000000..b52d062f6 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent-counts.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * How many child lifecycle events the tree recorded. + */ +export interface AgentSessionSubagentCounts { + 'spawned': number; + 'turns_started': number; + 'completed': number; + 'failed': number; + 'closed': number; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-closed.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-closed.ts new file mode 100644 index 000000000..a5396a395 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-closed.ts @@ -0,0 +1,25 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +export interface AgentSessionSubagentStatusClosed { + 'status': AgentSessionSubagentStatusClosedStatusEnum; +} + +export const AgentSessionSubagentStatusClosedStatusEnum = { + CLOSED: 'closed' +} as const; + +export type AgentSessionSubagentStatusClosedStatusEnum = typeof AgentSessionSubagentStatusClosedStatusEnum[keyof typeof AgentSessionSubagentStatusClosedStatusEnum]; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-completed.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-completed.ts new file mode 100644 index 000000000..869af8c88 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-completed.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +export interface AgentSessionSubagentStatusCompleted { + 'status': AgentSessionSubagentStatusCompletedStatusEnum; + 'success': boolean; + 'turns_used': number; +} + +export const AgentSessionSubagentStatusCompletedStatusEnum = { + COMPLETED: 'completed' +} as const; + +export type AgentSessionSubagentStatusCompletedStatusEnum = typeof AgentSessionSubagentStatusCompletedStatusEnum[keyof typeof AgentSessionSubagentStatusCompletedStatusEnum]; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-failed.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-failed.ts new file mode 100644 index 000000000..b45d826c8 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-failed.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentErrorData } from './agent-error-data'; + +export interface AgentSessionSubagentStatusFailed { + 'status': AgentSessionSubagentStatusFailedStatusEnum; + 'error': AgentErrorData; +} + +export const AgentSessionSubagentStatusFailedStatusEnum = { + FAILED: 'failed' +} as const; + +export type AgentSessionSubagentStatusFailedStatusEnum = typeof AgentSessionSubagentStatusFailedStatusEnum[keyof typeof AgentSessionSubagentStatusFailedStatusEnum]; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-running.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-running.ts new file mode 100644 index 000000000..645cb74ba --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status-running.ts @@ -0,0 +1,25 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +export interface AgentSessionSubagentStatusRunning { + 'status': AgentSessionSubagentStatusRunningStatusEnum; +} + +export const AgentSessionSubagentStatusRunningStatusEnum = { + RUNNING: 'running' +} as const; + +export type AgentSessionSubagentStatusRunningStatusEnum = typeof AgentSessionSubagentStatusRunningStatusEnum[keyof typeof AgentSessionSubagentStatusRunningStatusEnum]; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent-status.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status.ts new file mode 100644 index 000000000..5a0341f49 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent-status.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentErrorData } from './agent-error-data'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentStatusClosed } from './agent-session-subagent-status-closed'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentStatusCompleted } from './agent-session-subagent-status-completed'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentStatusFailed } from './agent-session-subagent-status-failed'; +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentStatusRunning } from './agent-session-subagent-status-running'; + +/** + * @type AgentSessionSubagentStatus + * Where a child stands. + */ +export type AgentSessionSubagentStatus = { status: 'closed' } & AgentSessionSubagentStatusClosed | { status: 'completed' } & AgentSessionSubagentStatusCompleted | { status: 'failed' } & AgentSessionSubagentStatusFailed | { status: 'running' } & AgentSessionSubagentStatusRunning; diff --git a/lib/packages/fabro-api-client/src/models/agent-session-subagent.ts b/lib/packages/fabro-api-client/src/models/agent-session-subagent.ts new file mode 100644 index 000000000..6710e1f66 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-subagent.ts @@ -0,0 +1,28 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AgentSessionSubagentStatus } from './agent-session-subagent-status'; + +/** + * One child the root spawned. A reused child stays one row; every event after the spawn moves its status. + */ +export interface AgentSessionSubagent { + 'agent_id': string; + 'depth': number; + 'task': string; + 'status': AgentSessionSubagentStatus; +} diff --git a/lib/packages/fabro-api-client/src/models/agent-session-tool-activity.ts b/lib/packages/fabro-api-client/src/models/agent-session-tool-activity.ts new file mode 100644 index 000000000..1a08d6996 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/agent-session-tool-activity.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * How one tool has been used across the tree. + */ +export interface AgentSessionToolActivity { + /** + * Calls started. + */ + 'calls': number; + /** + * Calls that completed as errors. + */ + 'errors': number; + /** + * Calls started and not yet completed. + */ + 'open': number; +} diff --git a/lib/packages/fabro-api-client/src/models/compaction-reason.ts b/lib/packages/fabro-api-client/src/models/compaction-reason.ts new file mode 100644 index 000000000..26fb5035a --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/compaction-reason.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Why a conversation compaction ran. + */ + +export const CompactionReason = { + THRESHOLD: 'threshold', + MANUAL: 'manual', + OVERFLOW: 'overflow' +} as const; + +export type CompactionReason = typeof CompactionReason[keyof typeof CompactionReason]; diff --git a/lib/packages/fabro-api-client/src/models/failover-continuation.ts b/lib/packages/fabro-api-client/src/models/failover-continuation.ts new file mode 100644 index 000000000..397341e47 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/failover-continuation.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * How a prompt carries on after a failover. `replay_prompt` when nothing the prompt committed is in the conversation, so the new route is asked the prompt again; `continue_turn` when the conversation holds output or tool results this prompt committed, so the new route continues the turn from where it stood. + */ + +export const FailoverContinuation = { + REPLAY_PROMPT: 'replay_prompt', + CONTINUE_TURN: 'continue_turn' +} as const; + +export type FailoverContinuation = typeof FailoverContinuation[keyof typeof FailoverContinuation]; diff --git a/lib/packages/fabro-api-client/src/models/failover-stop.ts b/lib/packages/fabro-api-client/src/models/failover-stop.ts new file mode 100644 index 000000000..50412cf28 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/failover-stop.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Why a model failure ends a prompt on its route when fallback routes were named. `ineligible` when the failure follows the request, so another route would fail the same way; `exhausted` when every named route has been taken. + */ + +export const FailoverStop = { + INELIGIBLE: 'ineligible', + EXHAUSTED: 'exhausted' +} as const; + +export type FailoverStop = typeof FailoverStop[keyof typeof FailoverStop]; diff --git a/lib/packages/fabro-api-client/src/models/index.ts b/lib/packages/fabro-api-client/src/models/index.ts index a0e1730fa..5771fc062 100644 --- a/lib/packages/fabro-api-client/src/models/index.ts +++ b/lib/packages/fabro-api-client/src/models/index.ts @@ -1,8 +1,29 @@ export * from './activated-skill'; export * from './agent-control-state'; +export * from './agent-error-data'; +export * from './agent-error-kind'; export * from './agent-event-props'; export * from './agent-mcp-tool-summary'; export * from './agent-session-activated-props'; +export * from './agent-session-activated-skill'; +export * from './agent-session-activity'; +export * from './agent-session-compaction'; +export * from './agent-session-descendant-account'; +export * from './agent-session-failover-stop'; +export * from './agent-session-mcp-server'; +export * from './agent-session-projection'; +export * from './agent-session-prompt-delta'; +export * from './agent-session-route'; +export * from './agent-session-route-failover'; +export * from './agent-session-skills'; +export * from './agent-session-subagent'; +export * from './agent-session-subagent-counts'; +export * from './agent-session-subagent-status'; +export * from './agent-session-subagent-status-closed'; +export * from './agent-session-subagent-status-completed'; +export * from './agent-session-subagent-status-failed'; +export * from './agent-session-subagent-status-running'; +export * from './agent-session-tool-activity'; export * from './agent-tools-available-props'; export * from './aggregate-billing'; export * from './aggregate-billing-totals'; @@ -51,6 +72,7 @@ export * from './close-run-pull-request-response'; export * from './code-location'; export * from './command-log-response'; export * from './command-termination'; +export * from './compaction-reason'; export * from './completion-content-part'; export * from './completion-cost'; export * from './completion-message'; @@ -118,6 +140,8 @@ export * from './exec-output-tail'; export * from './execute-query-request'; export * from './execute-query-response'; export * from './execute-query-response-rows-inner-inner'; +export * from './failover-continuation'; +export * from './failover-stop'; export * from './failure-category'; export * from './failure-detail'; export * from './failure-reason'; @@ -173,6 +197,10 @@ export * from './interview-provider-settings'; export * from './interview-question-record'; export * from './link-run-pull-request-request'; export * from './llm-output-kind'; +export * from './llm-retry-classification'; +export * from './llm-retry-classification-after'; +export * from './llm-retry-classification-never'; +export * from './llm-retry-classification-safe'; export * from './log-destination'; export * from './manifest-args'; export * from './manifest-config'; @@ -192,6 +220,7 @@ export * from './mcp-server-status'; export * from './mcp-server-status-disconnected'; export * from './mcp-server-status-failed'; export * from './mcp-server-status-ready'; +export * from './mcp-tool-summary'; export * from './mcp-transport'; export * from './mcp-transport-http'; export * from './mcp-transport-sandbox'; @@ -501,6 +530,7 @@ export * from './todo-list-kind'; export * from './todo-list-projection'; export * from './todo-projection'; export * from './todo-status'; +export * from './token-usage'; export * from './tool-category'; export * from './tool-source'; export * from './tool-source-application'; diff --git a/lib/packages/fabro-api-client/src/models/llm-retry-classification-after.ts b/lib/packages/fabro-api-client/src/models/llm-retry-classification-after.ts new file mode 100644 index 000000000..a05044dcd --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/llm-retry-classification-after.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Repeating the call is safe after the given delay. + */ +export interface LlmRetryClassificationAfter { + 'type': LlmRetryClassificationAfterTypeEnum; + /** + * The delay in milliseconds. + */ + 'after_millis': number; +} + +export const LlmRetryClassificationAfterTypeEnum = { + AFTER: 'after' +} as const; + +export type LlmRetryClassificationAfterTypeEnum = typeof LlmRetryClassificationAfterTypeEnum[keyof typeof LlmRetryClassificationAfterTypeEnum]; diff --git a/lib/packages/fabro-api-client/src/models/llm-retry-classification-never.ts b/lib/packages/fabro-api-client/src/models/llm-retry-classification-never.ts new file mode 100644 index 000000000..0729e7f9b --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/llm-retry-classification-never.ts @@ -0,0 +1,28 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Repeating the call cannot succeed. + */ +export interface LlmRetryClassificationNever { + 'type': LlmRetryClassificationNeverTypeEnum; +} + +export const LlmRetryClassificationNeverTypeEnum = { + NEVER: 'never' +} as const; + +export type LlmRetryClassificationNeverTypeEnum = typeof LlmRetryClassificationNeverTypeEnum[keyof typeof LlmRetryClassificationNeverTypeEnum]; diff --git a/lib/packages/fabro-api-client/src/models/llm-retry-classification-safe.ts b/lib/packages/fabro-api-client/src/models/llm-retry-classification-safe.ts new file mode 100644 index 000000000..d3f8da578 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/llm-retry-classification-safe.ts @@ -0,0 +1,28 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Repeating the call is safe on the caller\'s own schedule. + */ +export interface LlmRetryClassificationSafe { + 'type': LlmRetryClassificationSafeTypeEnum; +} + +export const LlmRetryClassificationSafeTypeEnum = { + SAFE: 'safe' +} as const; + +export type LlmRetryClassificationSafeTypeEnum = typeof LlmRetryClassificationSafeTypeEnum[keyof typeof LlmRetryClassificationSafeTypeEnum]; diff --git a/lib/packages/fabro-api-client/src/models/llm-retry-classification.ts b/lib/packages/fabro-api-client/src/models/llm-retry-classification.ts new file mode 100644 index 000000000..a75037097 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/llm-retry-classification.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { LlmRetryClassificationAfter } from './llm-retry-classification-after'; +// May contain unused imports in some cases +// @ts-ignore +import type { LlmRetryClassificationNever } from './llm-retry-classification-never'; +// May contain unused imports in some cases +// @ts-ignore +import type { LlmRetryClassificationSafe } from './llm-retry-classification-safe'; + +/** + * @type LlmRetryClassification + * Whether repeating the same resolved model call is safe. + */ +export type LlmRetryClassification = { type: 'after' } & LlmRetryClassificationAfter | { type: 'never' } & LlmRetryClassificationNever | { type: 'safe' } & LlmRetryClassificationSafe; diff --git a/lib/packages/fabro-api-client/src/models/mcp-tool-summary.ts b/lib/packages/fabro-api-client/src/models/mcp-tool-summary.ts new file mode 100644 index 000000000..21d752a04 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/mcp-tool-summary.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * One tool an MCP server advertised, as the coding agent\'s registry named it. + */ +export interface McpToolSummary { + /** + * The name the model calls: `mcp__{server}__{tool}`. + */ + 'name': string; + /** + * The server\'s own name for the tool. + */ + 'original_name': string; +} diff --git a/lib/packages/fabro-api-client/src/models/stage-projection.ts b/lib/packages/fabro-api-client/src/models/stage-projection.ts index aa97b10b4..ccf1d548f 100644 --- a/lib/packages/fabro-api-client/src/models/stage-projection.ts +++ b/lib/packages/fabro-api-client/src/models/stage-projection.ts @@ -18,6 +18,9 @@ import type { AgentControlState } from './agent-control-state'; // May contain unused imports in some cases // @ts-ignore +import type { AgentSessionProjection } from './agent-session-projection'; +// May contain unused imports in some cases +// @ts-ignore import type { BilledTokenCounts } from './billed-token-counts'; // May contain unused imports in some cases // @ts-ignore @@ -142,6 +145,7 @@ export interface StageProjection { * Whether the agent is executing normally or waiting for steering after an interrupt. */ 'agent_control': AgentControlState; + 'agent'?: AgentSessionProjection | null; /** * Lifecycle state of the stage projection. */ diff --git a/lib/packages/fabro-api-client/src/models/token-usage.ts b/lib/packages/fabro-api-client/src/models/token-usage.ts new file mode 100644 index 000000000..1a9946f89 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/token-usage.ts @@ -0,0 +1,41 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.2.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Token accounting as the coding agent counts it. The five buckets are disjoint: every token is counted in exactly one, so their plain sum is the total. A bucket that is absent reads as zero. + */ +export interface TokenUsage { + /** + * Prompt tokens that were neither read from nor written to a cache. + */ + 'input'?: number; + /** + * Completion tokens that are not reasoning tokens. + */ + 'output'?: number; + /** + * Completion tokens spent on reasoning, billed at the output rate. + */ + 'reasoning'?: number; + /** + * Prompt tokens served from a provider cache. + */ + 'cache_read'?: number; + /** + * Prompt tokens written into a provider cache. + */ + 'cache_write'?: number; +}