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:
Bryan Helmkamp 2026-03-06 08:33:51 -05:00
parent a4f119ad79
commit 681791db3f
18 changed files with 300 additions and 130 deletions

2
Cargo.lock generated
View file

@ -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",

View file

@ -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(

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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));
},
/**

View 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];

View file

@ -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.
*/

View file

@ -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;
}

View file

@ -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';

View file

@ -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.
*/

View file

@ -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;
/**

View file

@ -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;
}

View file

@ -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;

View 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];

View 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];