mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-06 08:18:58 +00:00
Improve Sessions API schema: discriminated turns, date-time formats, consistent naming
- Refactor SessionTurn into discriminated union (UserTurn, AssistantTurn, ToolTurn) using oneOf + discriminator so invalid states are unrepresentable - Add format: date-time to all timestamp fields for proper codegen types - Rename CreateSessionRequest.prompt to .content for consistency with SendMessageRequest - Change sendSessionMessage from 200 to 202 (async processing via SSE) - Extract inline response to SendMessageResponse schema - Add updated_at to SessionListItem for sort-by-activity support - Extract SessionId parameter to components/parameters (DRY) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a4f119ad79
commit
681791db3f
18 changed files with 300 additions and 130 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -142,6 +142,7 @@ dependencies = [
|
|||
"arc-workflows",
|
||||
"axum",
|
||||
"base64",
|
||||
"chrono",
|
||||
"clap",
|
||||
"dirs",
|
||||
"http-body-util",
|
||||
|
|
@ -308,6 +309,7 @@ dependencies = [
|
|||
name = "arc-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"prettyplease",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -31,21 +31,25 @@ export async function loader({ request, params }: Route.LoaderArgs) {
|
|||
model: apiSession.model,
|
||||
created_at: apiSession.created_at,
|
||||
updated_at: apiSession.updated_at,
|
||||
turns: apiSession.turns.map((t) => {
|
||||
if (t.kind === "tool" && t.tools) {
|
||||
return {
|
||||
kind: "tool" as const,
|
||||
tools: t.tools.map((tu) => ({
|
||||
id: tu.id,
|
||||
toolName: tu.tool_name,
|
||||
input: tu.input,
|
||||
result: tu.result,
|
||||
isError: tu.is_error,
|
||||
durationMs: tu.duration_ms,
|
||||
})),
|
||||
};
|
||||
turns: apiSession.turns.map((t): Turn => {
|
||||
switch (t.kind) {
|
||||
case "tool":
|
||||
return {
|
||||
kind: "tool",
|
||||
tools: t.tools.map((tu) => ({
|
||||
id: tu.id,
|
||||
toolName: tu.tool_name,
|
||||
input: tu.input,
|
||||
result: tu.result,
|
||||
isError: tu.is_error,
|
||||
durationMs: tu.duration_ms,
|
||||
})),
|
||||
};
|
||||
case "user":
|
||||
return { kind: "user", content: t.content, created_at: t.created_at };
|
||||
case "assistant":
|
||||
return { kind: "assistant", content: t.content };
|
||||
}
|
||||
return { kind: t.kind as "user" | "assistant", content: t.content ?? "", created_at: t.created_at };
|
||||
}),
|
||||
};
|
||||
const sessionGroups = groupSessionsByDate(
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ arc-llm = { path = "../arc-llm" }
|
|||
arc-util = { path = "../arc-util" }
|
||||
arc-db = { path = "../arc-db" }
|
||||
arc-types = { path = "../arc-types" }
|
||||
chrono.workspace = true
|
||||
axum = "0.8"
|
||||
dirs.workspace = true
|
||||
sqlx.workspace = true
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ pub async fn send_message_stub(
|
|||
State(_state): State<Arc<AppState>>,
|
||||
Path(_id): Path<String>,
|
||||
) -> Response {
|
||||
(StatusCode::OK, Json(serde_json::json!({"accepted": true}))).into_response()
|
||||
(StatusCode::ACCEPTED, Json(serde_json::json!({"accepted": true}))).into_response()
|
||||
}
|
||||
|
||||
pub async fn session_events_stub(
|
||||
|
|
@ -2366,48 +2366,61 @@ mod retros {
|
|||
|
||||
mod sessions {
|
||||
use arc_types::*;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
fn ts(s: &str) -> DateTime<Utc> {
|
||||
s.parse().unwrap()
|
||||
}
|
||||
|
||||
pub fn list_items() -> Vec<SessionListItem> {
|
||||
vec![
|
||||
SessionListItem {
|
||||
id: "s1".into(),
|
||||
title: "Add rate limiting to auth endpoints".into(),
|
||||
created_at: "2026-03-06T14:30:00Z".into(),
|
||||
created_at: ts("2026-03-06T14:30:00Z"),
|
||||
updated_at: ts("2026-03-06T15:45:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s2".into(),
|
||||
title: "Fix config parsing for nested values".into(),
|
||||
created_at: "2026-03-06T12:30:00Z".into(),
|
||||
created_at: ts("2026-03-06T12:30:00Z"),
|
||||
updated_at: ts("2026-03-06T13:15:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s3".into(),
|
||||
title: "Migrate to React Router v7".into(),
|
||||
created_at: "2026-03-05T10:00:00Z".into(),
|
||||
created_at: ts("2026-03-05T10:00:00Z"),
|
||||
updated_at: ts("2026-03-05T11:30:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s4".into(),
|
||||
title: "Add dark mode toggle".into(),
|
||||
created_at: "2026-03-05T09:00:00Z".into(),
|
||||
created_at: ts("2026-03-05T09:00:00Z"),
|
||||
updated_at: ts("2026-03-05T09:45:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s5".into(),
|
||||
title: "Update OpenAPI spec for v3".into(),
|
||||
created_at: "2026-03-05T08:00:00Z".into(),
|
||||
created_at: ts("2026-03-05T08:00:00Z"),
|
||||
updated_at: ts("2026-03-05T08:30:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s6".into(),
|
||||
title: "Terraform module for Redis cluster".into(),
|
||||
created_at: "2026-03-03T15:00:00Z".into(),
|
||||
created_at: ts("2026-03-03T15:00:00Z"),
|
||||
updated_at: ts("2026-03-03T16:00:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s7".into(),
|
||||
title: "Add pipeline event types".into(),
|
||||
created_at: "2026-03-01T11:00:00Z".into(),
|
||||
created_at: ts("2026-03-01T11:00:00Z"),
|
||||
updated_at: ts("2026-03-01T12:00:00Z"),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s8".into(),
|
||||
title: "Implement webhook retry logic".into(),
|
||||
created_at: "2026-02-28T09:00:00Z".into(),
|
||||
created_at: ts("2026-02-28T09:00:00Z"),
|
||||
updated_at: ts("2026-02-28T10:00:00Z"),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
@ -2415,50 +2428,50 @@ mod sessions {
|
|||
pub fn detail(id: &str) -> Option<SessionDetail> {
|
||||
match id {
|
||||
"s1" => Some(SessionDetail {
|
||||
id: "s1".into(), title: "Add rate limiting to auth endpoints".into(), model: "Opus 4.6".into(), created_at: "2026-03-06T14:30:00Z".into(), updated_at: "2026-03-06T15:45:00Z".into(),
|
||||
id: "s1".into(), title: "Add rate limiting to auth endpoints".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-06T14:30:00Z"), updated_at: ts("2026-03-06T15:45:00Z"),
|
||||
turns: vec![
|
||||
SessionTurn { kind: SessionTurnKind::User, content: Some("Add rate limiting to the auth endpoints. We're getting hit with brute force attempts on /api/auth/login and /api/auth/register. Use a sliding window approach with Redis, 10 requests per minute per IP.".into()), created_at: Some("2026-02-28T10:00:00Z".into()), tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("I'll implement sliding window rate limiting using Redis. Let me first look at the existing auth routes and middleware setup.".into()), created_at: None, tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Tool, content: None, created_at: None, tools: vec![
|
||||
SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "Add rate limiting to the auth endpoints. We're getting hit with brute force attempts on /api/auth/login and /api/auth/register. Use a sliding window approach with Redis, 10 requests per minute per IP.".into(), created_at: ts("2026-02-28T10:00:00Z") }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "I'll implement sliding window rate limiting using Redis. Let me first look at the existing auth routes and middleware setup.".into() }),
|
||||
SessionTurn::ToolTurn(ToolTurn { kind: ToolTurnKind::Tool, tools: vec![
|
||||
ToolUse { id: "toolu_03".into(), tool_name: "read_file".into(), input: r#"{ "path": "src/routes/auth.ts" }"#.into(), result: "import { Router } from \"express\";\nimport { loginHandler } from \"../handlers/auth\";\nimport { registerHandler } from \"../handlers/auth\";\n\nconst router = Router();\nrouter.post(\"/login\", loginHandler);\nrouter.post(\"/register\", registerHandler);\n\nexport default router;".into(), is_error: false, duration_ms: Some(52) },
|
||||
ToolUse { id: "toolu_04".into(), tool_name: "read_file".into(), input: r#"{ "path": "src/middleware/index.ts" }"#.into(), result: "export { authMiddleware } from \"./auth\";\nexport { corsMiddleware } from \"./cors\";\nexport { loggingMiddleware } from \"./logging\";".into(), is_error: false, duration_ms: Some(35) },
|
||||
ToolUse { id: "toolu_05".into(), tool_name: "search_files".into(), input: r#"{ "pattern": "redis", "path": "src/" }"#.into(), result: "src/lib/redis.ts:1: import Redis from \"ioredis\";\nsrc/lib/redis.ts:3: export const redis = new Redis(process.env.REDIS_URL);".into(), is_error: false, duration_ms: Some(180) },
|
||||
] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("Good \u{2014} there's already a Redis client at `src/lib/redis.ts`. I'll create a rate limiting middleware using the sliding window algorithm and apply it to the auth routes.\n\nHere's the plan:\n1. Create `src/middleware/rate-limit.ts` with sliding window logic\n2. Apply it to `/login` and `/register` routes\n3. Return `429 Too Many Requests` when the limit is exceeded".into()), created_at: None, tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Tool, content: None, created_at: None, tools: vec![
|
||||
] }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Good \u{2014} there's already a Redis client at `src/lib/redis.ts`. I'll create a rate limiting middleware using the sliding window algorithm and apply it to the auth routes.\n\nHere's the plan:\n1. Create `src/middleware/rate-limit.ts` with sliding window logic\n2. Apply it to `/login` and `/register` routes\n3. Return `429 Too Many Requests` when the limit is exceeded".into() }),
|
||||
SessionTurn::ToolTurn(ToolTurn { kind: ToolTurnKind::Tool, tools: vec![
|
||||
ToolUse { id: "toolu_06".into(), tool_name: "write_file".into(), input: r#"{ "path": "src/middleware/rate-limit.ts" }"#.into(), result: "File written: src/middleware/rate-limit.ts (47 lines)".into(), is_error: false, duration_ms: Some(62) },
|
||||
ToolUse { id: "toolu_07".into(), tool_name: "edit_file".into(), input: r#"{ "path": "src/routes/auth.ts", "operations": [{ "type": "insert", "line": 3 }] }"#.into(), result: "Applied 1 edit to src/routes/auth.ts".into(), is_error: false, duration_ms: Some(41) },
|
||||
ToolUse { id: "toolu_08".into(), tool_name: "edit_file".into(), input: r#"{ "path": "src/middleware/index.ts", "operations": [{ "type": "append" }] }"#.into(), result: "Applied 1 edit to src/middleware/index.ts".into(), is_error: false, duration_ms: Some(55) },
|
||||
] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("Done. I've created the rate limiter and wired it up:\n\n- **`src/middleware/rate-limit.ts`** \u{2014} Sliding window rate limiter using Redis sorted sets. Each request adds a timestamped entry, expired entries are pruned, and the count is checked against the limit.\n- **`src/routes/auth.ts`** \u{2014} Applied `rateLimit({ windowMs: 60_000, max: 10 })` to both `/login` and `/register`.\n- **`src/middleware/index.ts`** \u{2014} Re-exported the new middleware.\n\nWhen the limit is exceeded, clients get a `429` response with a `Retry-After` header indicating seconds until the window resets.".into()), created_at: None, tools: vec![] },
|
||||
] }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Done. I've created the rate limiter and wired it up:\n\n- **`src/middleware/rate-limit.ts`** \u{2014} Sliding window rate limiter using Redis sorted sets. Each request adds a timestamped entry, expired entries are pruned, and the count is checked against the limit.\n- **`src/routes/auth.ts`** \u{2014} Applied `rateLimit({ windowMs: 60_000, max: 10 })` to both `/login` and `/register`.\n- **`src/middleware/index.ts`** \u{2014} Re-exported the new middleware.\n\nWhen the limit is exceeded, clients get a `429` response with a `Retry-After` header indicating seconds until the window resets.".into() }),
|
||||
],
|
||||
}),
|
||||
"s2" => Some(SessionDetail {
|
||||
id: "s2".into(), title: "Fix config parsing for nested values".into(), model: "Sonnet 4.6".into(), created_at: "2026-03-06T12:30:00Z".into(), updated_at: "2026-03-06T13:15:00Z".into(),
|
||||
id: "s2".into(), title: "Fix config parsing for nested values".into(), model: "Sonnet 4.6".into(), created_at: ts("2026-03-06T12:30:00Z"), updated_at: ts("2026-03-06T13:15:00Z"),
|
||||
turns: vec![
|
||||
SessionTurn { kind: SessionTurnKind::User, content: Some("The CLI crashes when parsing nested TOML config values like [database.connection]. Can you debug and fix this?".into()), created_at: Some("2026-02-28T10:00:00Z".into()), tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("Let me look at the config parser to understand how nested keys are handled.".into()), created_at: None, tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Tool, content: None, created_at: None, tools: vec![
|
||||
SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "The CLI crashes when parsing nested TOML config values like [database.connection]. Can you debug and fix this?".into(), created_at: ts("2026-02-28T10:00:00Z") }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Let me look at the config parser to understand how nested keys are handled.".into() }),
|
||||
SessionTurn::ToolTurn(ToolTurn { kind: ToolTurnKind::Tool, tools: vec![
|
||||
ToolUse { id: "toolu_09".into(), tool_name: "read_file".into(), input: r#"{ "path": "src/config/parser.ts" }"#.into(), result: "export function parseConfig(raw: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of raw.split(\"\\n\")) {\n const match = line.match(/^(\\w+)\\s*=\\s*(.+)$/);\n if (match) result[match[1]] = match[2].trim();\n }\n return result;\n}".into(), is_error: false, duration_ms: Some(67) },
|
||||
] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("Found the issue. The parser uses a flat `Record<string, string>` and the regex `^(\\w+)` only matches simple keys \u{2014} it can't handle TOML section headers like `[database.connection]` or dotted keys like `database.host`.\n\nI'll rewrite the parser to support nested sections and dotted keys, producing a properly nested object.".into()), created_at: None, tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Tool, content: None, created_at: None, tools: vec![
|
||||
] }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Found the issue. The parser uses a flat `Record<string, string>` and the regex `^(\\w+)` only matches simple keys \u{2014} it can't handle TOML section headers like `[database.connection]` or dotted keys like `database.host`.\n\nI'll rewrite the parser to support nested sections and dotted keys, producing a properly nested object.".into() }),
|
||||
SessionTurn::ToolTurn(ToolTurn { kind: ToolTurnKind::Tool, tools: vec![
|
||||
ToolUse { id: "toolu_10".into(), tool_name: "write_file".into(), input: r#"{ "path": "src/config/parser.ts" }"#.into(), result: "File written: src/config/parser.ts (62 lines)".into(), is_error: false, duration_ms: Some(78) },
|
||||
ToolUse { id: "toolu_11".into(), tool_name: "run_command".into(), input: r#"{ "command": "npm test -- --grep 'config parser'" }"#.into(), result: "PASS src/config/__tests__/parser.test.ts\n config parser\n \u{2713} parses flat keys (2ms)\n \u{2713} parses section headers (1ms)\n \u{2713} parses dotted keys (1ms)\n \u{2713} handles nested sections (1ms)\n\nTest Suites: 1 passed\nTests: 4 passed".into(), is_error: false, duration_ms: Some(2150) },
|
||||
] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("Fixed. The parser now:\n\n1. Tracks the current section header (e.g. `[database.connection]`)\n2. Constructs nested objects from dotted paths\n3. Handles both `key = value` within sections and bare `dotted.key = value` syntax\n\nAll 4 tests pass, including the new nested section case.".into()), created_at: None, tools: vec![] },
|
||||
] }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "Fixed. The parser now:\n\n1. Tracks the current section header (e.g. `[database.connection]`)\n2. Constructs nested objects from dotted paths\n3. Handles both `key = value` within sections and bare `dotted.key = value` syntax\n\nAll 4 tests pass, including the new nested section case.".into() }),
|
||||
],
|
||||
}),
|
||||
"s3" => Some(SessionDetail {
|
||||
id: "s3".into(), title: "Migrate to React Router v7".into(), model: "Opus 4.6".into(), created_at: "2026-03-05T10:00:00Z".into(), updated_at: "2026-03-05T11:30:00Z".into(),
|
||||
id: "s3".into(), title: "Migrate to React Router v7".into(), model: "Opus 4.6".into(), created_at: ts("2026-03-05T10:00:00Z"), updated_at: ts("2026-03-05T11:30:00Z"),
|
||||
turns: vec![
|
||||
SessionTurn { kind: SessionTurnKind::User, content: Some("Help me migrate our app from React Router v6 to v7. We're using createBrowserRouter with data loaders.".into()), created_at: Some("2026-02-26T10:00:00Z".into()), tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("I'll audit your current router setup and identify what needs to change for v7. Let me scan the codebase.".into()), created_at: None, tools: vec![] },
|
||||
SessionTurn { kind: SessionTurnKind::Tool, content: None, created_at: None, tools: vec![
|
||||
SessionTurn::UserTurn(UserTurn { kind: UserTurnKind::User, content: "Help me migrate our app from React Router v6 to v7. We're using createBrowserRouter with data loaders.".into(), created_at: ts("2026-02-26T10:00:00Z") }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "I'll audit your current router setup and identify what needs to change for v7. Let me scan the codebase.".into() }),
|
||||
SessionTurn::ToolTurn(ToolTurn { kind: ToolTurnKind::Tool, tools: vec![
|
||||
ToolUse { id: "toolu_12".into(), tool_name: "search_files".into(), input: r#"{ "pattern": "createBrowserRouter" }"#.into(), result: "src/main.tsx:5: import { createBrowserRouter } from \"react-router-dom\";\nsrc/main.tsx:8: const router = createBrowserRouter([".into(), is_error: false, duration_ms: Some(220) },
|
||||
ToolUse { id: "toolu_13".into(), tool_name: "read_file".into(), input: r#"{ "path": "package.json" }"#.into(), result: "\"react-router-dom\": \"^6.22.0\"".into(), is_error: false, duration_ms: Some(30) },
|
||||
] },
|
||||
SessionTurn { kind: SessionTurnKind::Assistant, content: Some("You're on React Router 6.22. The migration to v7 involves:\n\n1. Replace `react-router-dom` with `react-router` (single package)\n2. Update `createBrowserRouter` import path\n3. Migrate data loaders to the new `Route.LoaderArgs` type\n4. Update any `useLoaderData` calls to be type-safe\n\nLet me make these changes.".into()), created_at: None, tools: vec![] },
|
||||
] }),
|
||||
SessionTurn::AssistantTurn(AssistantTurn { kind: AssistantTurnKind::Assistant, content: "You're on React Router 6.22. The migration to v7 involves:\n\n1. Replace `react-router-dom` with `react-router` (single package)\n2. Update `createBrowserRouter` import path\n3. Migrate data loaders to the new `Route.LoaderArgs` type\n4. Update any `useLoaderData` calls to be type-safe\n\nLet me make these changes.".into() }),
|
||||
],
|
||||
}),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ description = "Generated Rust types from the Arc API OpenAPI spec"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
|
|
|
|||
|
|
@ -763,13 +763,7 @@ paths:
|
|||
summary: Retrieve Session
|
||||
description: Returns the full session detail including all conversation turns.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Unique session identifier.
|
||||
schema:
|
||||
type: string
|
||||
example: s1
|
||||
- $ref: "#/components/parameters/SessionId"
|
||||
responses:
|
||||
"200":
|
||||
description: Session detail
|
||||
|
|
@ -791,13 +785,7 @@ paths:
|
|||
summary: Send Session Message
|
||||
description: Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Unique session identifier.
|
||||
schema:
|
||||
type: string
|
||||
example: s1
|
||||
- $ref: "#/components/parameters/SessionId"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
|
|
@ -805,19 +793,12 @@ paths:
|
|||
schema:
|
||||
$ref: "#/components/schemas/SendMessageRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: Message accepted
|
||||
"202":
|
||||
description: Message accepted for processing
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
accepted:
|
||||
type: boolean
|
||||
description: Whether the message was accepted for processing.
|
||||
example: true
|
||||
required:
|
||||
- accepted
|
||||
$ref: "#/components/schemas/SendMessageResponse"
|
||||
"404":
|
||||
description: Session not found
|
||||
content:
|
||||
|
|
@ -832,13 +813,7 @@ paths:
|
|||
summary: Stream Session Events
|
||||
description: Opens a server-sent event (SSE) stream for real-time session updates. Events include new assistant turns, tool invocations, and completion signals.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Unique session identifier.
|
||||
schema:
|
||||
type: string
|
||||
example: s1
|
||||
- $ref: "#/components/parameters/SessionId"
|
||||
responses:
|
||||
"200":
|
||||
description: Server-sent event stream
|
||||
|
|
@ -1080,6 +1055,15 @@ components:
|
|||
schema:
|
||||
type: string
|
||||
|
||||
SessionId:
|
||||
name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Unique session identifier.
|
||||
schema:
|
||||
type: string
|
||||
example: s1
|
||||
|
||||
PageLimit:
|
||||
name: page[limit]
|
||||
in: query
|
||||
|
|
@ -2091,6 +2075,7 @@ components:
|
|||
- id
|
||||
- title
|
||||
- created_at
|
||||
- updated_at
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
|
|
@ -2102,35 +2087,78 @@ components:
|
|||
example: Add rate limiting to auth endpoints
|
||||
created_at:
|
||||
type: string
|
||||
description: ISO 8601 timestamp when the session was created.
|
||||
format: date-time
|
||||
description: Timestamp when the session was created.
|
||||
example: "2026-03-06T14:30:00Z"
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when the session was last updated (e.g. new turn added).
|
||||
example: "2026-03-06T15:45:00Z"
|
||||
|
||||
|
||||
SessionTurn:
|
||||
description: A single turn in a session conversation — a user message, assistant response, or tool invocation block.
|
||||
discriminator:
|
||||
propertyName: kind
|
||||
mapping:
|
||||
user: "#/components/schemas/UserTurn"
|
||||
assistant: "#/components/schemas/AssistantTurn"
|
||||
tool: "#/components/schemas/ToolTurn"
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/UserTurn"
|
||||
- $ref: "#/components/schemas/AssistantTurn"
|
||||
- $ref: "#/components/schemas/ToolTurn"
|
||||
|
||||
UserTurn:
|
||||
description: A user message turn.
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
- content
|
||||
- created_at
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
description: The type of turn.
|
||||
enum:
|
||||
- user
|
||||
- assistant
|
||||
- tool
|
||||
example: user
|
||||
enum: [user]
|
||||
content:
|
||||
type: string
|
||||
description: Text content of the turn. Present for user and assistant turns, absent for tool turns.
|
||||
description: Text content of the user message.
|
||||
example: Add rate limiting to the auth endpoints using a sliding window approach with Redis.
|
||||
created_at:
|
||||
type: string
|
||||
description: ISO 8601 timestamp when the turn was created. Typically present for user turns.
|
||||
format: date-time
|
||||
description: Timestamp when the turn was created.
|
||||
example: "2026-02-28T10:00:00Z"
|
||||
|
||||
AssistantTurn:
|
||||
description: An assistant response turn.
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
- content
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
enum: [assistant]
|
||||
content:
|
||||
type: string
|
||||
description: Text content of the assistant response.
|
||||
example: I'll implement sliding window rate limiting using Redis.
|
||||
|
||||
ToolTurn:
|
||||
description: A tool invocation turn.
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
- tools
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
enum: [tool]
|
||||
tools:
|
||||
type: array
|
||||
description: Tool invocations for this turn. Present only when kind is "tool".
|
||||
description: Tool invocations for this turn.
|
||||
items:
|
||||
$ref: "#/components/schemas/ToolUse"
|
||||
|
||||
|
|
@ -2159,11 +2187,13 @@ components:
|
|||
example: Opus 4.6
|
||||
created_at:
|
||||
type: string
|
||||
description: ISO 8601 timestamp when the session was created.
|
||||
format: date-time
|
||||
description: Timestamp when the session was created.
|
||||
example: "2026-03-06T14:30:00Z"
|
||||
updated_at:
|
||||
type: string
|
||||
description: ISO 8601 timestamp when the session was last updated (e.g. new turn added).
|
||||
format: date-time
|
||||
description: Timestamp when the session was last updated (e.g. new turn added).
|
||||
example: "2026-03-06T15:45:00Z"
|
||||
turns:
|
||||
type: array
|
||||
|
|
@ -2175,9 +2205,9 @@ components:
|
|||
description: Request body for starting a new session.
|
||||
type: object
|
||||
required:
|
||||
- prompt
|
||||
- content
|
||||
properties:
|
||||
prompt:
|
||||
content:
|
||||
type: string
|
||||
description: The initial user message to start the session.
|
||||
example: Add rate limiting to the auth endpoints using a sliding window approach with Redis, 10 requests per minute per IP.
|
||||
|
|
@ -2199,7 +2229,8 @@ components:
|
|||
example: s42
|
||||
created_at:
|
||||
type: string
|
||||
description: ISO 8601 timestamp when the session was created.
|
||||
format: date-time
|
||||
description: Timestamp when the session was created.
|
||||
example: "2026-03-06T16:00:00Z"
|
||||
|
||||
SendMessageRequest:
|
||||
|
|
@ -2213,6 +2244,17 @@ components:
|
|||
description: The user message text.
|
||||
example: Can you also add a bypass for internal health-check IPs?
|
||||
|
||||
SendMessageResponse:
|
||||
description: Acknowledgement that the message was accepted for asynchronous processing.
|
||||
type: object
|
||||
required:
|
||||
- accepted
|
||||
properties:
|
||||
accepted:
|
||||
type: boolean
|
||||
description: Whether the message was accepted for processing.
|
||||
example: true
|
||||
|
||||
# ── Insights Schemas ────────────────────────────────────────────────
|
||||
|
||||
SavedQuery:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ index.ts
|
|||
models/aggregate-usage.ts
|
||||
models/api-question-option.ts
|
||||
models/api-question.ts
|
||||
models/assistant-turn.ts
|
||||
models/branch.ts
|
||||
models/cancel-run200-response.ts
|
||||
models/check-run-status.ts
|
||||
|
|
@ -75,7 +76,7 @@ models/run-verification.ts
|
|||
models/save-query-request.ts
|
||||
models/saved-query.ts
|
||||
models/send-message-request.ts
|
||||
models/send-session-message200-response.ts
|
||||
models/send-message-response.ts
|
||||
models/session-detail.ts
|
||||
models/session-list-item.ts
|
||||
models/session-turn.ts
|
||||
|
|
@ -92,11 +93,13 @@ models/steer-request.ts
|
|||
models/steer-run200-response.ts
|
||||
models/submit-answer-request.ts
|
||||
models/submit-answer-response.ts
|
||||
models/tool-turn.ts
|
||||
models/tool-use.ts
|
||||
models/usage-by-model.ts
|
||||
models/usage-stage.ts
|
||||
models/usage-totals.ts
|
||||
models/user-response.ts
|
||||
models/user-turn.ts
|
||||
models/verification-category.ts
|
||||
models/verification-control.ts
|
||||
models/verification-detail-response.ts
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import type { PaginatedSessionList } from '../models';
|
|||
// @ts-ignore
|
||||
import type { SendMessageRequest } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SendSessionMessage200Response } from '../models';
|
||||
import type { SendMessageResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SessionDetail } from '../models';
|
||||
/**
|
||||
|
|
@ -314,7 +314,7 @@ export const SessionsApiFp = function(configuration?: Configuration) {
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SendSessionMessage200Response>> {
|
||||
async sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SendMessageResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.sendSessionMessage(id, sendMessageRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.sendSessionMessage']?.[localVarOperationServerIndex]?.url;
|
||||
|
|
@ -381,7 +381,7 @@ export const SessionsApiFactory = function (configuration?: Configuration, baseP
|
|||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): AxiosPromise<SendSessionMessage200Response> {
|
||||
sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): AxiosPromise<SendMessageResponse> {
|
||||
return localVarFp.sendSessionMessage(id, sendMessageRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
34
packages/arc-api-client/src/models/assistant-turn.ts
Normal file
34
packages/arc-api-client/src/models/assistant-turn.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Arc Run API
|
||||
* HTTP API for managing Arc workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An assistant response turn.
|
||||
*/
|
||||
export interface AssistantTurn {
|
||||
'kind': AssistantTurnKindEnum;
|
||||
/**
|
||||
* Text content of the assistant response.
|
||||
*/
|
||||
'content': string;
|
||||
}
|
||||
|
||||
export const AssistantTurnKindEnum = {
|
||||
ASSISTANT: 'assistant'
|
||||
} as const;
|
||||
|
||||
export type AssistantTurnKindEnum = typeof AssistantTurnKindEnum[keyof typeof AssistantTurnKindEnum];
|
||||
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ export interface CreateSessionRequest {
|
|||
/**
|
||||
* The initial user message to start the session.
|
||||
*/
|
||||
'prompt': string;
|
||||
'content': string;
|
||||
/**
|
||||
* LLM model to use. If omitted, the server default is used.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export interface CreateSessionResponse {
|
|||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was created.
|
||||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
export * from './aggregate-usage';
|
||||
export * from './api-question';
|
||||
export * from './api-question-option';
|
||||
export * from './assistant-turn';
|
||||
export * from './branch';
|
||||
export * from './cancel-run200-response';
|
||||
export * from './check-run';
|
||||
|
|
@ -56,7 +57,7 @@ export * from './run-verification-control';
|
|||
export * from './save-query-request';
|
||||
export * from './saved-query';
|
||||
export * from './send-message-request';
|
||||
export * from './send-session-message200-response';
|
||||
export * from './send-message-response';
|
||||
export * from './session-detail';
|
||||
export * from './session-list-item';
|
||||
export * from './session-turn';
|
||||
|
|
@ -73,11 +74,13 @@ export * from './steer-request';
|
|||
export * from './steer-run200-response';
|
||||
export * from './submit-answer-request';
|
||||
export * from './submit-answer-response';
|
||||
export * from './tool-turn';
|
||||
export * from './tool-use';
|
||||
export * from './usage-by-model';
|
||||
export * from './usage-stage';
|
||||
export * from './usage-totals';
|
||||
export * from './user-response';
|
||||
export * from './user-turn';
|
||||
export * from './verification-category';
|
||||
export * from './verification-control';
|
||||
export * from './verification-detail-response';
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@
|
|||
|
||||
|
||||
|
||||
export interface SendSessionMessage200Response {
|
||||
/**
|
||||
* Acknowledgement that the message was accepted for asynchronous processing.
|
||||
*/
|
||||
export interface SendMessageResponse {
|
||||
/**
|
||||
* Whether the message was accepted for processing.
|
||||
*/
|
||||
|
|
@ -34,11 +34,11 @@ export interface SessionDetail {
|
|||
*/
|
||||
'model': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was created.
|
||||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was last updated (e.g. new turn added).
|
||||
* Timestamp when the session was last updated (e.g. new turn added).
|
||||
*/
|
||||
'updated_at': string;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -27,8 +27,12 @@ export interface SessionListItem {
|
|||
*/
|
||||
'title': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was created.
|
||||
* Timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* Timestamp when the session was last updated (e.g. new turn added).
|
||||
*/
|
||||
'updated_at': string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,38 +13,23 @@
|
|||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { AssistantTurn } from './assistant-turn';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ToolTurn } from './tool-turn';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { ToolUse } from './tool-use';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { UserTurn } from './user-turn';
|
||||
|
||||
/**
|
||||
* @type SessionTurn
|
||||
* A single turn in a session conversation — a user message, assistant response, or tool invocation block.
|
||||
*/
|
||||
export interface SessionTurn {
|
||||
/**
|
||||
* The type of turn.
|
||||
*/
|
||||
'kind': SessionTurnKindEnum;
|
||||
/**
|
||||
* Text content of the turn. Present for user and assistant turns, absent for tool turns.
|
||||
*/
|
||||
'content'?: string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the turn was created. Typically present for user turns.
|
||||
*/
|
||||
'created_at'?: string;
|
||||
/**
|
||||
* Tool invocations for this turn. Present only when kind is \"tool\".
|
||||
*/
|
||||
'tools'?: Array<ToolUse>;
|
||||
}
|
||||
|
||||
export const SessionTurnKindEnum = {
|
||||
USER: 'user',
|
||||
ASSISTANT: 'assistant',
|
||||
TOOL: 'tool'
|
||||
} as const;
|
||||
|
||||
export type SessionTurnKindEnum = typeof SessionTurnKindEnum[keyof typeof SessionTurnKindEnum];
|
||||
export type SessionTurn = { kind: 'assistant' } & AssistantTurn | { kind: 'tool' } & ToolTurn | { kind: 'user' } & UserTurn;
|
||||
|
||||
|
||||
|
|
|
|||
37
packages/arc-api-client/src/models/tool-turn.ts
Normal file
37
packages/arc-api-client/src/models/tool-turn.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Arc Run API
|
||||
* HTTP API for managing Arc workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.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 { ToolUse } from './tool-use';
|
||||
|
||||
/**
|
||||
* A tool invocation turn.
|
||||
*/
|
||||
export interface ToolTurn {
|
||||
'kind': ToolTurnKindEnum;
|
||||
/**
|
||||
* Tool invocations for this turn.
|
||||
*/
|
||||
'tools': Array<ToolUse>;
|
||||
}
|
||||
|
||||
export const ToolTurnKindEnum = {
|
||||
TOOL: 'tool'
|
||||
} as const;
|
||||
|
||||
export type ToolTurnKindEnum = typeof ToolTurnKindEnum[keyof typeof ToolTurnKindEnum];
|
||||
|
||||
|
||||
38
packages/arc-api-client/src/models/user-turn.ts
Normal file
38
packages/arc-api-client/src/models/user-turn.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Arc Run API
|
||||
* HTTP API for managing Arc workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A user message turn.
|
||||
*/
|
||||
export interface UserTurn {
|
||||
'kind': UserTurnKindEnum;
|
||||
/**
|
||||
* Text content of the user message.
|
||||
*/
|
||||
'content': string;
|
||||
/**
|
||||
* Timestamp when the turn was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
export const UserTurnKindEnum = {
|
||||
USER: 'user'
|
||||
} as const;
|
||||
|
||||
export type UserTurnKindEnum = typeof UserTurnKindEnum[keyof typeof UserTurnKindEnum];
|
||||
|
||||
|
||||
Loading…
Add table
Reference in a new issue