mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-11 22:53:00 +00:00
Enrich Sessions API: add ToolUse fields, timestamps, descriptions, and flatten list endpoint
- Add id, is_error, duration_ms to ToolUse; rename args to input - Add created_at/updated_at timestamps to session schemas; replace time/date display strings - Add descriptions and examples to all Sessions API fields and endpoints - Flatten List Sessions response from grouped SessionGroup[] to SessionListItem[] - Move date grouping (Today/Yesterday/etc.) to React client via groupSessionsByDate() - Symlink docs/api-reference/arc-api.yaml to canonical openapi/arc-api.yaml - Update React ToolRow components with duration display and error styling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e4babddd5e
commit
e569770c36
20 changed files with 397 additions and 2564 deletions
|
|
@ -11,3 +11,47 @@ export function timeAgo(iso: string): string {
|
|||
const days = Math.floor(hours / 24);
|
||||
return `${days}d ago`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human-readable date label for grouping (e.g. "Today", "Yesterday", "Previous 7 days").
|
||||
*/
|
||||
function dateLabel(iso: string): string {
|
||||
const now = new Date();
|
||||
const date = new Date(iso);
|
||||
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const startOfYesterday = new Date(startOfToday.getTime() - 86_400_000);
|
||||
const startOf7DaysAgo = new Date(startOfToday.getTime() - 7 * 86_400_000);
|
||||
|
||||
if (date >= startOfToday) return "Today";
|
||||
if (date >= startOfYesterday) return "Yesterday";
|
||||
if (date >= startOf7DaysAgo) return "Previous 7 days";
|
||||
return "Older";
|
||||
}
|
||||
|
||||
interface SessionItem {
|
||||
id: string;
|
||||
title: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface SessionGroup {
|
||||
label: string;
|
||||
sessions: SessionItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Group a flat list of sessions (already sorted newest-first) into date-labeled groups.
|
||||
*/
|
||||
export function groupSessionsByDate(sessions: SessionItem[]): SessionGroup[] {
|
||||
const groups: SessionGroup[] = [];
|
||||
let current: SessionGroup | undefined;
|
||||
for (const s of sessions) {
|
||||
const label = dateLabel(s.created_at);
|
||||
if (!current || current.label !== label) {
|
||||
current = { label, sessions: [] };
|
||||
groups.push(current);
|
||||
}
|
||||
current.sessions.push(s);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,9 +47,12 @@ const statusConfig: Record<StageStatus, { icon: typeof CheckCircleIcon; color: s
|
|||
};
|
||||
|
||||
interface ToolUse {
|
||||
id: string;
|
||||
toolName: string;
|
||||
args: string;
|
||||
input: string;
|
||||
result: string;
|
||||
isError: boolean;
|
||||
durationMs?: number;
|
||||
}
|
||||
|
||||
type TurnType =
|
||||
|
|
@ -72,17 +75,18 @@ function ToolRow({ tool }: { tool: ToolUse }) {
|
|||
<ChevronRightIcon className={`size-3 shrink-0 text-fg-muted transition-transform duration-150 ${open ? "rotate-90" : ""}`} />
|
||||
<WrenchScrewdriverIcon className="size-3.5 shrink-0 text-fg-muted" />
|
||||
<span className="font-mono text-xs text-fg-3">{tool.toolName}</span>
|
||||
<span className="truncate font-mono text-xs text-fg-muted">{tool.args}</span>
|
||||
{tool.durationMs != null && <span className="text-[11px] text-fg-muted">{tool.durationMs}ms</span>}
|
||||
<span className="truncate font-mono text-xs text-fg-muted">{tool.input}</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="space-y-px bg-overlay px-2.5 pb-2 pt-1">
|
||||
<div className="rounded bg-overlay px-2.5 py-2">
|
||||
<div className="mb-1 text-[10px] font-medium uppercase tracking-wider text-fg-muted">Args</div>
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs leading-relaxed text-fg-3">{tool.args}</pre>
|
||||
<div className="mb-1 text-[10px] font-medium uppercase tracking-wider text-fg-muted">Input</div>
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs leading-relaxed text-fg-3">{tool.input}</pre>
|
||||
</div>
|
||||
<div className="rounded bg-overlay px-2.5 py-2">
|
||||
<div className="mb-1 text-[10px] font-medium uppercase tracking-wider text-fg-muted">Result</div>
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs leading-relaxed text-fg-3">{tool.result}</pre>
|
||||
<pre className={`whitespace-pre-wrap font-mono text-xs leading-relaxed ${tool.isError ? "text-coral" : "text-fg-3"}`}>{tool.result}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -137,9 +141,12 @@ export default function RunStages({ loaderData }: Route.ComponentProps) {
|
|||
return {
|
||||
kind: "tool" as const,
|
||||
tools: t.tools.map((tu) => ({
|
||||
id: tu.id,
|
||||
toolName: tu.tool_name,
|
||||
args: tu.args,
|
||||
input: tu.input,
|
||||
result: tu.result,
|
||||
isError: tu.is_error,
|
||||
durationMs: tu.duration_ms,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import {
|
|||
UserIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { timeAgo } from "../lib/time";
|
||||
import { timeAgo, groupSessionsByDate } from "../lib/time";
|
||||
import { apiJson } from "../api-client";
|
||||
import type { SessionDetail as ApiSessionDetail, PaginatedSessionGroupList } from "@qltysh/arc-api-client";
|
||||
import type { SessionDetail as ApiSessionDetail, PaginatedSessionList } from "@qltysh/arc-api-client";
|
||||
import type { Route } from "./+types/session-detail";
|
||||
|
||||
export const handle = { hideHeader: true, wide: true };
|
||||
|
|
@ -21,9 +21,9 @@ export function meta({}: Route.MetaArgs) {
|
|||
}
|
||||
|
||||
export async function loader({ request, params }: Route.LoaderArgs) {
|
||||
const [apiSession, { data: apiGroups }] = await Promise.all([
|
||||
const [apiSession, { data: apiSessions }] = await Promise.all([
|
||||
apiJson<ApiSessionDetail>(`/sessions/${params.sessionId}`, { request }),
|
||||
apiJson<PaginatedSessionGroupList>("/sessions", { request }),
|
||||
apiJson<PaginatedSessionList>("/sessions", { request }),
|
||||
]);
|
||||
const session: Session = {
|
||||
id: apiSession.id,
|
||||
|
|
@ -36,30 +36,31 @@ export async function loader({ request, params }: Route.LoaderArgs) {
|
|||
return {
|
||||
kind: "tool" as const,
|
||||
tools: t.tools.map((tu) => ({
|
||||
id: tu.id,
|
||||
toolName: tu.tool_name,
|
||||
args: tu.args,
|
||||
input: tu.input,
|
||||
result: tu.result,
|
||||
isError: tu.is_error,
|
||||
durationMs: tu.duration_ms,
|
||||
})),
|
||||
};
|
||||
}
|
||||
return { kind: t.kind as "user" | "assistant", content: t.content ?? "", created_at: t.created_at };
|
||||
}),
|
||||
};
|
||||
const sessionGroups = apiGroups.map((g) => ({
|
||||
label: g.label,
|
||||
sessions: g.sessions.map((s) => ({
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
created_at: s.created_at,
|
||||
})),
|
||||
}));
|
||||
const sessionGroups = groupSessionsByDate(
|
||||
apiSessions.map((s) => ({ id: s.id, title: s.title, created_at: s.created_at }))
|
||||
);
|
||||
return { session, sessionGroups };
|
||||
}
|
||||
|
||||
interface ToolUse {
|
||||
id: string;
|
||||
toolName: string;
|
||||
args: string;
|
||||
input: string;
|
||||
result: string;
|
||||
isError: boolean;
|
||||
durationMs?: number;
|
||||
}
|
||||
|
||||
type Turn =
|
||||
|
|
@ -98,19 +99,28 @@ const sessions: Record<string, Session> = {
|
|||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s1_01",
|
||||
toolName: "read_file",
|
||||
args: `{ "path": "src/routes/auth.ts" }`,
|
||||
input: `{ "path": "src/routes/auth.ts" }`,
|
||||
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;`,
|
||||
isError: false,
|
||||
durationMs: 45,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_02",
|
||||
toolName: "read_file",
|
||||
args: `{ "path": "src/middleware/index.ts" }`,
|
||||
input: `{ "path": "src/middleware/index.ts" }`,
|
||||
result: `export { authMiddleware } from "./auth";\nexport { corsMiddleware } from "./cors";\nexport { loggingMiddleware } from "./logging";`,
|
||||
isError: false,
|
||||
durationMs: 38,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_03",
|
||||
toolName: "search_files",
|
||||
args: `{ "pattern": "redis", "path": "src/" }`,
|
||||
input: `{ "pattern": "redis", "path": "src/" }`,
|
||||
result: `src/lib/redis.ts:1: import Redis from "ioredis";\nsrc/lib/redis.ts:3: export const redis = new Redis(process.env.REDIS_URL);`,
|
||||
isError: false,
|
||||
durationMs: 210,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -122,19 +132,28 @@ const sessions: Record<string, Session> = {
|
|||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s1_04",
|
||||
toolName: "write_file",
|
||||
args: `{ "path": "src/middleware/rate-limit.ts" }`,
|
||||
input: `{ "path": "src/middleware/rate-limit.ts" }`,
|
||||
result: `File written: src/middleware/rate-limit.ts (47 lines)`,
|
||||
isError: false,
|
||||
durationMs: 65,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_05",
|
||||
toolName: "edit_file",
|
||||
args: `{ "path": "src/routes/auth.ts", "operations": [{ "type": "insert", "line": 3 }] }`,
|
||||
input: `{ "path": "src/routes/auth.ts", "operations": [{ "type": "insert", "line": 3 }] }`,
|
||||
result: `Applied 1 edit to src/routes/auth.ts`,
|
||||
isError: false,
|
||||
durationMs: 42,
|
||||
},
|
||||
{
|
||||
id: "toolu_s1_06",
|
||||
toolName: "edit_file",
|
||||
args: `{ "path": "src/middleware/index.ts", "operations": [{ "type": "append" }] }`,
|
||||
input: `{ "path": "src/middleware/index.ts", "operations": [{ "type": "append" }] }`,
|
||||
result: `Applied 1 edit to src/middleware/index.ts`,
|
||||
isError: false,
|
||||
durationMs: 55,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -164,9 +183,12 @@ const sessions: Record<string, Session> = {
|
|||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s2_01",
|
||||
toolName: "read_file",
|
||||
args: `{ "path": "src/config/parser.ts" }`,
|
||||
input: `{ "path": "src/config/parser.ts" }`,
|
||||
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}`,
|
||||
isError: false,
|
||||
durationMs: 52,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -178,14 +200,20 @@ const sessions: Record<string, Session> = {
|
|||
kind: "tool",
|
||||
tools: [
|
||||
{
|
||||
id: "toolu_s2_02",
|
||||
toolName: "write_file",
|
||||
args: `{ "path": "src/config/parser.ts" }`,
|
||||
input: `{ "path": "src/config/parser.ts" }`,
|
||||
result: `File written: src/config/parser.ts (62 lines)`,
|
||||
isError: false,
|
||||
durationMs: 78,
|
||||
},
|
||||
{
|
||||
id: "toolu_s2_03",
|
||||
toolName: "run_command",
|
||||
args: `{ "command": "npm test -- --grep 'config parser'" }`,
|
||||
input: `{ "command": "npm test -- --grep 'config parser'" }`,
|
||||
result: `PASS src/config/__tests__/parser.test.ts\n config parser\n ✓ parses flat keys (2ms)\n ✓ parses section headers (1ms)\n ✓ parses dotted keys (1ms)\n ✓ handles nested sections (1ms)\n\nTest Suites: 1 passed\nTests: 4 passed`,
|
||||
isError: false,
|
||||
durationMs: 2150,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -207,8 +235,8 @@ const sessions: Record<string, Session> = {
|
|||
{
|
||||
kind: "tool",
|
||||
tools: [
|
||||
{ toolName: "search_files", args: `{ "pattern": "createBrowserRouter" }`, result: `src/main.tsx:5: import { createBrowserRouter } from "react-router-dom";\nsrc/main.tsx:8: const router = createBrowserRouter([` },
|
||||
{ toolName: "read_file", args: `{ "path": "package.json" }`, result: `"react-router-dom": "^6.22.0"` },
|
||||
{ id: "toolu_s3_01", toolName: "search_files", input: `{ "pattern": "createBrowserRouter" }`, result: `src/main.tsx:5: import { createBrowserRouter } from "react-router-dom";\nsrc/main.tsx:8: const router = createBrowserRouter([`, isError: false, durationMs: 180 },
|
||||
{ id: "toolu_s3_02", toolName: "read_file", input: `{ "path": "package.json" }`, result: `"react-router-dom": "^6.22.0"`, isError: false, durationMs: 35 },
|
||||
],
|
||||
},
|
||||
{ kind: "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." },
|
||||
|
|
@ -275,17 +303,18 @@ function ToolRow({ tool }: { tool: ToolUse }) {
|
|||
<ChevronRightIcon className={`size-3 shrink-0 text-fg-muted transition-transform duration-150 ${open ? "rotate-90" : ""}`} />
|
||||
<WrenchScrewdriverIcon className="size-3.5 shrink-0 text-fg-muted" />
|
||||
<span className="font-mono text-xs text-fg-3">{tool.toolName}</span>
|
||||
<span className="truncate font-mono text-xs text-fg-muted">{tool.args}</span>
|
||||
{tool.durationMs != null && <span className="text-[11px] text-fg-muted">{tool.durationMs}ms</span>}
|
||||
<span className="truncate font-mono text-xs text-fg-muted">{tool.input}</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="space-y-px bg-overlay px-2.5 pb-2 pt-1">
|
||||
<div className="rounded bg-overlay px-2.5 py-2">
|
||||
<div className="mb-1 text-[10px] font-medium uppercase tracking-wider text-fg-muted">Args</div>
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs leading-relaxed text-fg-3">{tool.args}</pre>
|
||||
<div className="mb-1 text-[10px] font-medium uppercase tracking-wider text-fg-muted">Input</div>
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs leading-relaxed text-fg-3">{tool.input}</pre>
|
||||
</div>
|
||||
<div className="rounded bg-overlay px-2.5 py-2">
|
||||
<div className="mb-1 text-[10px] font-medium uppercase tracking-wider text-fg-muted">Result</div>
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs leading-relaxed text-fg-3">{tool.result}</pre>
|
||||
<pre className={`whitespace-pre-wrap font-mono text-xs leading-relaxed ${tool.isError ? "text-coral" : "text-fg-3"}`}>{tool.result}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import {
|
|||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Link } from "react-router";
|
||||
import { timeAgo } from "../lib/time";
|
||||
import { timeAgo, groupSessionsByDate } from "../lib/time";
|
||||
import { apiJson } from "../api-client";
|
||||
import { getAppConfig } from "../lib/config.server";
|
||||
import type { PaginatedProjectList, PaginatedSessionGroupList } from "@qltysh/arc-api-client";
|
||||
import type { PaginatedProjectList, PaginatedSessionList } from "@qltysh/arc-api-client";
|
||||
import type { Route } from "./+types/start";
|
||||
|
||||
export const handle = { hideHeader: true, wide: true };
|
||||
|
|
@ -36,17 +36,12 @@ export async function loader({ request }: Route.LoaderArgs) {
|
|||
const { feature_flags } = getAppConfig();
|
||||
const [{ data: apiProjects }, { data: apiSessions }] = await Promise.all([
|
||||
apiJson<PaginatedProjectList>("/projects", { request }),
|
||||
apiJson<PaginatedSessionGroupList>("/sessions", { request }),
|
||||
apiJson<PaginatedSessionList>("/sessions", { request }),
|
||||
]);
|
||||
const projects = apiProjects.map((p) => ({ id: p.id, name: p.name }));
|
||||
const sessionGroups = apiSessions.map((g) => ({
|
||||
label: g.label,
|
||||
sessions: g.sessions.map((s) => ({
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
created_at: s.created_at,
|
||||
})),
|
||||
}));
|
||||
const sessionGroups = groupSessionsByDate(
|
||||
apiSessions.map((s) => ({ id: s.id, title: s.title, created_at: s.created_at }))
|
||||
);
|
||||
return { projects, sessionGroups, feature_flags };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ pub async fn list_sessions(
|
|||
State(_state): State<Arc<AppState>>,
|
||||
Query(pagination): Query<PaginationParams>,
|
||||
) -> Response {
|
||||
paginated_response(sessions::groups(), &pagination)
|
||||
paginated_response(sessions::list_items(), &pagination)
|
||||
}
|
||||
|
||||
pub async fn create_session_stub(
|
||||
|
|
@ -1033,8 +1033,8 @@ mod runs {
|
|||
StageTurn {
|
||||
kind: StageTurnKind::Tool, content: None,
|
||||
tools: vec![
|
||||
ToolUse { tool_name: "read_file".into(), args: r#"{ "path": "environments/production/config.toml" }"#.into(), result: "[redis]\nhost = \"redis-prod.internal\"\nport = 6379".into() },
|
||||
ToolUse { tool_name: "read_file".into(), args: r#"{ "path": "environments/staging/config.toml" }"#.into(), result: "[redis]\nhost = \"redis-staging.internal\"\nport = 6379".into() },
|
||||
ToolUse { id: "toolu_01".into(), tool_name: "read_file".into(), input: r#"{ "path": "environments/production/config.toml" }"#.into(), result: "[redis]\nhost = \"redis-prod.internal\"\nport = 6379".into(), is_error: false, duration_ms: Some(45) },
|
||||
ToolUse { id: "toolu_02".into(), tool_name: "read_file".into(), input: r#"{ "path": "environments/staging/config.toml" }"#.into(), result: "[redis]\nhost = \"redis-staging.internal\"\nport = 6379".into(), is_error: false, duration_ms: Some(38) },
|
||||
],
|
||||
},
|
||||
StageTurn { kind: StageTurnKind::Assistant, content: Some("I've detected drift in 3 resources between production and staging:\n\n1. **redis.max_connections** — production has 200, staging has 100\n2. **redis.tls** — enabled in production, disabled in staging\n3. **iam.session_duration** — production uses 3600s, staging uses 1800s".into()), tools: vec![] },
|
||||
|
|
@ -2367,62 +2367,47 @@ mod retros {
|
|||
mod sessions {
|
||||
use arc_types::*;
|
||||
|
||||
pub fn groups() -> Vec<SessionGroup> {
|
||||
pub fn list_items() -> Vec<SessionListItem> {
|
||||
vec![
|
||||
SessionGroup {
|
||||
label: "Today".into(),
|
||||
sessions: vec![
|
||||
SessionListItem {
|
||||
id: "s1".into(),
|
||||
title: "Add rate limiting to auth endpoints".into(),
|
||||
created_at: "2026-03-06T14:30:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s2".into(),
|
||||
title: "Fix config parsing for nested values".into(),
|
||||
created_at: "2026-03-06T12:30:00Z".into(),
|
||||
},
|
||||
],
|
||||
SessionListItem {
|
||||
id: "s1".into(),
|
||||
title: "Add rate limiting to auth endpoints".into(),
|
||||
created_at: "2026-03-06T14:30:00Z".into(),
|
||||
},
|
||||
SessionGroup {
|
||||
label: "Yesterday".into(),
|
||||
sessions: vec![
|
||||
SessionListItem {
|
||||
id: "s3".into(),
|
||||
title: "Migrate to React Router v7".into(),
|
||||
created_at: "2026-03-05T10:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s4".into(),
|
||||
title: "Add dark mode toggle".into(),
|
||||
created_at: "2026-03-05T09:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s5".into(),
|
||||
title: "Update OpenAPI spec for v3".into(),
|
||||
created_at: "2026-03-05T08:00:00Z".into(),
|
||||
},
|
||||
],
|
||||
SessionListItem {
|
||||
id: "s2".into(),
|
||||
title: "Fix config parsing for nested values".into(),
|
||||
created_at: "2026-03-06T12:30:00Z".into(),
|
||||
},
|
||||
SessionGroup {
|
||||
label: "Previous 7 days".into(),
|
||||
sessions: vec![
|
||||
SessionListItem {
|
||||
id: "s6".into(),
|
||||
title: "Terraform module for Redis cluster".into(),
|
||||
created_at: "2026-03-03T15:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s7".into(),
|
||||
title: "Add pipeline event types".into(),
|
||||
created_at: "2026-03-01T11:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s8".into(),
|
||||
title: "Implement webhook retry logic".into(),
|
||||
created_at: "2026-02-28T09:00:00Z".into(),
|
||||
},
|
||||
],
|
||||
SessionListItem {
|
||||
id: "s3".into(),
|
||||
title: "Migrate to React Router v7".into(),
|
||||
created_at: "2026-03-05T10:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s4".into(),
|
||||
title: "Add dark mode toggle".into(),
|
||||
created_at: "2026-03-05T09:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s5".into(),
|
||||
title: "Update OpenAPI spec for v3".into(),
|
||||
created_at: "2026-03-05T08:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s6".into(),
|
||||
title: "Terraform module for Redis cluster".into(),
|
||||
created_at: "2026-03-03T15:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s7".into(),
|
||||
title: "Add pipeline event types".into(),
|
||||
created_at: "2026-03-01T11:00:00Z".into(),
|
||||
},
|
||||
SessionListItem {
|
||||
id: "s8".into(),
|
||||
title: "Implement webhook retry logic".into(),
|
||||
created_at: "2026-02-28T09:00:00Z".into(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
@ -2435,15 +2420,15 @@ mod sessions {
|
|||
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![
|
||||
ToolUse { tool_name: "read_file".into(), args: 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() },
|
||||
ToolUse { tool_name: "read_file".into(), args: r#"{ "path": "src/middleware/index.ts" }"#.into(), result: "export { authMiddleware } from \"./auth\";\nexport { corsMiddleware } from \"./cors\";\nexport { loggingMiddleware } from \"./logging\";".into() },
|
||||
ToolUse { tool_name: "search_files".into(), args: 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() },
|
||||
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![
|
||||
ToolUse { tool_name: "write_file".into(), args: r#"{ "path": "src/middleware/rate-limit.ts" }"#.into(), result: "File written: src/middleware/rate-limit.ts (47 lines)".into() },
|
||||
ToolUse { tool_name: "edit_file".into(), args: r#"{ "path": "src/routes/auth.ts", "operations": [{ "type": "insert", "line": 3 }] }"#.into(), result: "Applied 1 edit to src/routes/auth.ts".into() },
|
||||
ToolUse { tool_name: "edit_file".into(), args: r#"{ "path": "src/middleware/index.ts", "operations": [{ "type": "append" }] }"#.into(), result: "Applied 1 edit to src/middleware/index.ts".into() },
|
||||
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![] },
|
||||
],
|
||||
|
|
@ -2454,12 +2439,12 @@ mod sessions {
|
|||
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![
|
||||
ToolUse { tool_name: "read_file".into(), args: 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() },
|
||||
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![
|
||||
ToolUse { tool_name: "write_file".into(), args: r#"{ "path": "src/config/parser.ts" }"#.into(), result: "File written: src/config/parser.ts (62 lines)".into() },
|
||||
ToolUse { tool_name: "run_command".into(), args: 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() },
|
||||
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![] },
|
||||
],
|
||||
|
|
@ -2470,8 +2455,8 @@ mod sessions {
|
|||
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![
|
||||
ToolUse { tool_name: "search_files".into(), args: r#"{ "pattern": "createBrowserRouter" }"#.into(), result: "src/main.tsx:5: import { createBrowserRouter } from \"react-router-dom\";\nsrc/main.tsx:8: const router = createBrowserRouter([".into() },
|
||||
ToolUse { tool_name: "read_file".into(), args: r#"{ "path": "package.json" }"#.into(), result: "\"react-router-dom\": \"^6.22.0\"".into() },
|
||||
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![] },
|
||||
],
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
1
docs/api-reference/arc-api.yaml
Symbolic link
1
docs/api-reference/arc-api.yaml
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../openapi/arc-api.yaml
|
||||
|
|
@ -719,17 +719,17 @@ paths:
|
|||
operationId: listSessions
|
||||
tags: [Sessions]
|
||||
summary: List Sessions
|
||||
description: Returns sessions grouped by recency (e.g. "Today", "Yesterday"). Each group contains a flat list of session summaries.
|
||||
description: Returns sessions ordered by recency (newest first).
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/PageLimit"
|
||||
- $ref: "#/components/parameters/PageOffset"
|
||||
responses:
|
||||
"200":
|
||||
description: Paginated list of session groups
|
||||
description: Paginated list of sessions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PaginatedSessionGroupList"
|
||||
$ref: "#/components/schemas/PaginatedSessionList"
|
||||
post:
|
||||
operationId: createSession
|
||||
tags: [Sessions]
|
||||
|
|
@ -1120,7 +1120,7 @@ components:
|
|||
meta:
|
||||
$ref: "#/components/schemas/PaginationMeta"
|
||||
|
||||
PaginatedSessionGroupList:
|
||||
PaginatedSessionList:
|
||||
type: object
|
||||
required:
|
||||
- data
|
||||
|
|
@ -1129,7 +1129,7 @@ components:
|
|||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/SessionGroup"
|
||||
$ref: "#/components/schemas/SessionListItem"
|
||||
meta:
|
||||
$ref: "#/components/schemas/PaginationMeta"
|
||||
|
||||
|
|
@ -1478,25 +1478,39 @@ components:
|
|||
type: string
|
||||
|
||||
ToolUse:
|
||||
description: A single tool invocation with its arguments and result.
|
||||
description: A single tool invocation with its input, result, and execution metadata.
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- tool_name
|
||||
- args
|
||||
- input
|
||||
- result
|
||||
- is_error
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Unique identifier for this tool invocation. Enables correlation in parallel tool use.
|
||||
example: toolu_01A09q90qw90lq917835lq9
|
||||
tool_name:
|
||||
type: string
|
||||
description: Name of the tool that was invoked.
|
||||
example: read_file
|
||||
args:
|
||||
input:
|
||||
type: string
|
||||
description: JSON-encoded arguments passed to the tool.
|
||||
description: JSON-encoded input passed to the tool.
|
||||
example: '{ "path": "src/routes/auth.ts" }'
|
||||
result:
|
||||
type: string
|
||||
description: Output returned by the tool.
|
||||
description: Output returned by the tool. Contains the error message when is_error is true.
|
||||
example: 'import { Router } from "express";'
|
||||
is_error:
|
||||
type: boolean
|
||||
description: Whether the tool invocation failed. When true, the result field contains the error message.
|
||||
example: false
|
||||
duration_ms:
|
||||
type: integer
|
||||
description: Wall-clock execution time of the tool invocation in milliseconds.
|
||||
example: 142
|
||||
|
||||
StageTurn:
|
||||
type: object
|
||||
|
|
@ -2064,22 +2078,6 @@ components:
|
|||
description: ISO 8601 timestamp when the session was created.
|
||||
example: "2026-03-06T14:30:00Z"
|
||||
|
||||
SessionGroup:
|
||||
description: A group of sessions sharing a time-based label (e.g. "Today", "Yesterday").
|
||||
type: object
|
||||
required:
|
||||
- label
|
||||
- sessions
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
description: Human-readable group heading.
|
||||
example: Today
|
||||
sessions:
|
||||
type: array
|
||||
description: Sessions belonging to this group, ordered by recency.
|
||||
items:
|
||||
$ref: "#/components/schemas/SessionListItem"
|
||||
|
||||
SessionTurn:
|
||||
description: A single turn in a session conversation — a user message, assistant response, or tool invocation block.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ models/paginated-run-list.ts
|
|||
models/paginated-run-stage-list.ts
|
||||
models/paginated-run-verification-list.ts
|
||||
models/paginated-saved-query-list.ts
|
||||
models/paginated-session-group-list.ts
|
||||
models/paginated-session-list.ts
|
||||
models/paginated-stage-turn-list.ts
|
||||
models/paginated-verification-category-list.ts
|
||||
models/paginated-workflow-list.ts
|
||||
|
|
@ -75,8 +75,8 @@ 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/session-detail.ts
|
||||
models/session-group.ts
|
||||
models/session-list-item.ts
|
||||
models/session-turn.ts
|
||||
models/setting-field-type.ts
|
||||
|
|
|
|||
|
|
@ -28,20 +28,20 @@ import type { CreateSessionResponse } from '../models';
|
|||
// @ts-ignore
|
||||
import type { ErrorResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { PaginatedSessionGroupList } from '../models';
|
||||
import type { PaginatedSessionList } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SendMessageRequest } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SessionDetail } from '../models';
|
||||
import type { SendSessionMessage200Response } from '../models';
|
||||
// @ts-ignore
|
||||
import type { SteerRun200Response } from '../models';
|
||||
import type { SessionDetail } from '../models';
|
||||
/**
|
||||
* SessionsApi - axios parameter creator
|
||||
*/
|
||||
export const SessionsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -76,7 +76,7 @@ export const SessionsApiAxiosParamCreator = function (configuration?: Configurat
|
|||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit]
|
||||
* @param {number} [pageOffset]
|
||||
|
|
@ -116,9 +116,9 @@ export const SessionsApiAxiosParamCreator = function (configuration?: Configurat
|
|||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -150,9 +150,9 @@ export const SessionsApiAxiosParamCreator = function (configuration?: Configurat
|
|||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
|
|
@ -189,9 +189,9 @@ export const SessionsApiAxiosParamCreator = function (configuration?: Configurat
|
|||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Events include new assistant turns, tool invocations, and completion signals.
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -232,7 +232,7 @@ export const SessionsApiFp = function(configuration?: Configuration) {
|
|||
const localVarAxiosParamCreator = SessionsApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -245,23 +245,23 @@ export const SessionsApiFp = function(configuration?: Configuration) {
|
|||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit]
|
||||
* @param {number} [pageOffset]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedSessionGroupList>> {
|
||||
async listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PaginatedSessionList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSessions(pageLimit, pageOffset, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.listSessions']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -272,23 +272,23 @@ export const SessionsApiFp = function(configuration?: Configuration) {
|
|||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SteerRun200Response>> {
|
||||
async sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SendSessionMessage200Response>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.sendSessionMessage(id, sendMessageRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['SessionsApi.sendSessionMessage']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Events include new assistant turns, tool invocations, and completion signals.
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -308,7 +308,7 @@ export const SessionsApiFactory = function (configuration?: Configuration, baseP
|
|||
const localVarFp = SessionsApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -318,20 +318,20 @@ export const SessionsApiFactory = function (configuration?: Configuration, baseP
|
|||
return localVarFp.createSession(createSessionRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit]
|
||||
* @param {number} [pageOffset]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedSessionGroupList> {
|
||||
listSessions(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedSessionList> {
|
||||
return localVarFp.listSessions(pageLimit, pageOffset, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -339,20 +339,20 @@ export const SessionsApiFactory = function (configuration?: Configuration, baseP
|
|||
return localVarFp.retrieveSession(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): AxiosPromise<SteerRun200Response> {
|
||||
sendSessionMessage(id: string, sendMessageRequest: SendMessageRequest, options?: RawAxiosRequestConfig): AxiosPromise<SendSessionMessage200Response> {
|
||||
return localVarFp.sendSessionMessage(id, sendMessageRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Events include new assistant turns, tool invocations, and completion signals.
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -367,7 +367,7 @@ export const SessionsApiFactory = function (configuration?: Configuration, baseP
|
|||
*/
|
||||
export class SessionsApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* Start a new interactive chat session. The initial user prompt is required; a model may optionally be specified.
|
||||
* @summary Create Session
|
||||
* @param {CreateSessionRequest} createSessionRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -378,7 +378,7 @@ export class SessionsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns sessions ordered by recency (newest first).
|
||||
* @summary List Sessions
|
||||
* @param {number} [pageLimit]
|
||||
* @param {number} [pageOffset]
|
||||
|
|
@ -390,9 +390,9 @@ export class SessionsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the full session detail including all conversation turns.
|
||||
* @summary Retrieve Session
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -401,9 +401,9 @@ export class SessionsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Append a user message to an existing session. The server will process it and produce assistant and tool turns asynchronously via the event stream.
|
||||
* @summary Send Session Message
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {SendMessageRequest} sendMessageRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
|
|
@ -413,9 +413,9 @@ export class SessionsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Opens a server-sent event (SSE) stream for real-time session updates. Events include new assistant turns, tool invocations, and completion signals.
|
||||
* @summary Stream Session Events
|
||||
* @param {string} id
|
||||
* @param {string} id Unique session identifier.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,8 +14,17 @@
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Request body for starting a new session.
|
||||
*/
|
||||
export interface CreateSessionRequest {
|
||||
/**
|
||||
* The initial user message to start the session.
|
||||
*/
|
||||
'prompt': string;
|
||||
/**
|
||||
* LLM model to use. If omitted, the server default is used.
|
||||
*/
|
||||
'model'?: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,17 @@
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Response returned after successfully creating a session.
|
||||
*/
|
||||
export interface CreateSessionResponse {
|
||||
/**
|
||||
* Unique identifier for the newly created session.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export * from './paginated-run-list';
|
|||
export * from './paginated-run-stage-list';
|
||||
export * from './paginated-run-verification-list';
|
||||
export * from './paginated-saved-query-list';
|
||||
export * from './paginated-session-group-list';
|
||||
export * from './paginated-session-list';
|
||||
export * from './paginated-stage-turn-list';
|
||||
export * from './paginated-verification-category-list';
|
||||
export * from './paginated-workflow-list';
|
||||
|
|
@ -56,8 +56,8 @@ 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 './session-detail';
|
||||
export * from './session-group';
|
||||
export * from './session-list-item';
|
||||
export * from './session-turn';
|
||||
export * from './setting-field';
|
||||
|
|
|
|||
27
packages/arc-api-client/src/models/paginated-session-list.ts
Normal file
27
packages/arc-api-client/src/models/paginated-session-list.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* 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 { PaginationMeta } from './pagination-meta';
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { SessionListItem } from './session-list-item';
|
||||
|
||||
export interface PaginatedSessionList {
|
||||
'data': Array<SessionListItem>;
|
||||
'meta': PaginationMeta;
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,13 @@
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Request body for sending a follow-up message in an existing session.
|
||||
*/
|
||||
export interface SendMessageRequest {
|
||||
/**
|
||||
* The user message text.
|
||||
*/
|
||||
'content': string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface SendSessionMessage200Response {
|
||||
/**
|
||||
* Whether the message was accepted for processing.
|
||||
*/
|
||||
'accepted': boolean;
|
||||
}
|
||||
|
||||
|
|
@ -17,12 +17,33 @@
|
|||
// @ts-ignore
|
||||
import type { SessionTurn } from './session-turn';
|
||||
|
||||
/**
|
||||
* Full session record including metadata and the complete conversation history.
|
||||
*/
|
||||
export interface SessionDetail {
|
||||
/**
|
||||
* Unique session identifier.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* Short title summarizing the session topic.
|
||||
*/
|
||||
'title': string;
|
||||
/**
|
||||
* The LLM model used for this session.
|
||||
*/
|
||||
'model': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was last updated (e.g. new turn added).
|
||||
*/
|
||||
'updated_at': string;
|
||||
/**
|
||||
* Ordered list of conversation turns.
|
||||
*/
|
||||
'turns': Array<SessionTurn>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,17 @@
|
|||
// @ts-ignore
|
||||
import type { SessionListItem } from './session-list-item';
|
||||
|
||||
/**
|
||||
* A group of sessions sharing a time-based label (e.g. \"Today\", \"Yesterday\").
|
||||
*/
|
||||
export interface SessionGroup {
|
||||
/**
|
||||
* Human-readable group heading.
|
||||
*/
|
||||
'label': string;
|
||||
/**
|
||||
* Sessions belonging to this group, ordered by recency.
|
||||
*/
|
||||
'sessions': Array<SessionListItem>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,21 @@
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Summary of a session shown in list views.
|
||||
*/
|
||||
export interface SessionListItem {
|
||||
/**
|
||||
* Unique session identifier.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* Short title summarizing the session topic.
|
||||
*/
|
||||
'title': string;
|
||||
/**
|
||||
* ISO 8601 timestamp when the session was created.
|
||||
*/
|
||||
'created_at': string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,25 @@
|
|||
// @ts-ignore
|
||||
import type { ToolUse } from './tool-use';
|
||||
|
||||
/**
|
||||
* 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>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,33 @@
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* A single tool invocation with its input, result, and execution metadata.
|
||||
*/
|
||||
export interface ToolUse {
|
||||
/**
|
||||
* Unique identifier for this tool invocation. Enables correlation in parallel tool use.
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
* Name of the tool that was invoked.
|
||||
*/
|
||||
'tool_name': string;
|
||||
'args': string;
|
||||
/**
|
||||
* JSON-encoded input passed to the tool.
|
||||
*/
|
||||
'input': string;
|
||||
/**
|
||||
* Output returned by the tool. Contains the error message when is_error is true.
|
||||
*/
|
||||
'result': string;
|
||||
/**
|
||||
* Whether the tool invocation failed. When true, the result field contains the error message.
|
||||
*/
|
||||
'is_error': boolean;
|
||||
/**
|
||||
* Wall-clock execution time of the tool invocation in milliseconds.
|
||||
*/
|
||||
'duration_ms'?: number;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue