Skip to content

Latest commit

 

History

History
299 lines (236 loc) · 11.9 KB

File metadata and controls

299 lines (236 loc) · 11.9 KB

@astra/sdk

TypeScript SDK for the Astra agent runtime: JWT auth, sessions, runs, run list, multi-agent delegation, session lifecycle (update / close / resume / cancel / activity), reflect and decision-trace, events (session timeline and causal chains), edge connection status, memory, §5.5 edge callbacks, SSE (POST /chat/stream), and WebSocket (/chat/ws).

Supported paths match the currently registered Rust runtime routes and astra-thin-client (no /api prefix by default). Use pathPrefix if your gateway mounts the API under a prefix (for example /api → https://host/api/auth/login). Legacy task/plan/agent-job helpers remain in the clients for source compatibility with older servers, but the current runtime intentionally returns 404 for those routes and they are not runtime capabilities.

Distribution: This package is versioned in the Astra monorepo and configured as a public scoped npm package. Publishing remains an explicit release action; no repository workflow publishes it automatically. The Web app consumes the workspace copy via file:../packages/sdk (see web/package.json).

Installation

npm install @astra/sdk

Before the first npm release, consume file:../packages/sdk from a checkout or install a locally generated pack tarball.

Quick start

REST client (direct to astra-server)

import { AstraClient } from '@astra/sdk';

const client = new AstraClient({ baseUrl: 'http://localhost:17001' });

const auth = await client.login('alice', 'password');
// auth: access_token, refresh_token, token_type, expires_in

const session = await client.createSession();
// session.sessionId, createdAt, lastActive (normalized from server snake_case)

const run = await client.createRun({
  message: 'Hello',
  sessionId: session.sessionId,
});

register(username, password, { email?, displayName? }) sends email; if omitted, a placeholder {username}@users.local.astra is used so the server’s required field is satisfied.

Runs, delegation, and session observability

For HTTP chat streaming, modelSelection: "auto" opts into the Server's configured model-routing policy. Explicit { offeringId: "..." } selection keeps its existing behavior. Auto requires a qualified Server-catalog pair; provider model gateways and WebSocket chat continue to use explicit Offerings. See model routing.

// List durable runs (GET /runs)
const firstPage = await client.listRuns({ limit: 20 });
const secondPage = firstPage.nextCursor
  ? await client.listRuns({ limit: 20, cursor: firstPage.nextCursor })
  : null;

// Sub-runs for a parent run (GET /chat/runs/{id}/delegations)
const { sub_run_ids } = await client.listDelegations(parentRunId);

// Delegate to multiple agents (POST /chat/runs/{id}/delegate) — body matches server DelegationRequest
const me = await client.getMe();
await client.delegateRun(parentRunId, {
  delegation_id: crypto.randomUUID(),
  parent_run_id: parentRunId,
  task: 'Review and summarize',
  pattern: { sequential: { agent_ids: ['agent-a', 'agent-b'], stop_on_success: false, timeout_sec: 0 } },
  user_id: me.user_id,
  depth: 0,
  context: {},
});

await client.pauseDelegations(parentRunId);
await client.resumeDelegations(parentRunId);

// Session ops + activity log
await client.updateSession(sessionId, { title: 'Renamed' });
await client.closeSession(sessionId);
const activity = await client.getSessionActivity(sessionId, { limit: 50 });

// Cancel execution without deleting history. A pending response is not success:
// keep showing "Stopping" and repeat this operation until executionSettled.
const cancellation = await client.cancelSession(sessionId);
if (!cancellation.executionSettled) {
  console.log('Stopping; execution is not yet confirmed idle', cancellation.runs);
}

// Reflect / tool-selection evidence (GET /chat/session/.../reflect | decision-trace)
const report = await client.getSessionReflect(sessionId, { focus: 'auto', last_n: 20, question: '' });

// Event pipeline (GET /events/session/... , GET /events?... , GET /events/causal-chain/...)
const timeline = await client.getSessionEvents(sessionId, { limit: 100 });
const filtered = await client.listEvents({ sessionId, eventType: 'tool_result', limit: 50 });
const chain = await client.getCausalChain(causalChainId);

// Connected edge agents (GET /edges/status)
const { edges } = await client.getEdgesStatus();

Delegation requires a configured delegation engine on the server; otherwise the API returns 503 — the SDK forwards errors as AstraApiError.

Streaming (SSE)

streamChat uses POST /chat/stream with a JSON body (the Web dashboard's typed BFF integration lives in web/app/api/chats/[chatId]/stream/route.ts).

const stream = client.streamChat(
  { message: 'Explain quantum computing', sessionId: 'sess-1' },
  {
    onEvent(event) {
      if (event.type === 'text_delta') {
        process.stdout.write(event.content);
      }
    },
  },
);

// stream.close() to cancel

WebSocket

The runtime exposes /chat/ws (not under /api unless you set pathPrefix).

import { AstraWebSocket } from '@astra/sdk';

const ws = new AstraWebSocket({
  url: 'ws://localhost:17001/chat/ws',
  token: auth.access_token,
});

await ws.connect();
ws.on('text_delta', (event) => console.log(event.content));
ws.sendMessage('Build a REST API', { sessionId: 'sess-1' });

Browser apps and the BFF pattern

Do not expose long-lived refresh tokens in browser-accessible JavaScript. The dashboard (web/) keeps tokens in httpOnly cookies and routes browser requests through explicit Next.js Route Handlers such as /api/chats, /api/models, /api/skills, and /api/chats/{id}/stream. Those BFF handlers construct an authenticated AstraClient server-side and call the runtime with typed SDK methods. Avoid adding a generic browser-visible runtime proxy; add a small typed BFF route instead when a new browser surface needs runtime access.

Gateway prefix

const client = new AstraClient({
  baseUrl: 'https://api.example.com',
  pathPrefix: '/v1',
});
// e.g. login → https://api.example.com/v1/auth/login

React hooks

import { useAstraChat } from '@astra/sdk/react';
import { AstraClient } from '@astra/sdk';

const client = new AstraClient({
  baseUrl: 'http://localhost:17001',
  accessToken: token,
});

function Chat() {
  const {
    messages,
    sendMessage,
    isStreaming,
    plan,
    tasks,
    agents,
    toolCalls,
    usage,
  } = useAstraChat({
    client,
    agentId: 'optional-agent',
    model: 'optional-model',
    executionBudget: { initialTurns: 4, hardTurnLimit: 24 },
    capabilities: ['multi_agent', 'reflect'],
    allowTools: ['github'],
    explain: true,
  });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}
      <aside>
        {tasks.map((task) => (
          <div key={task.id}>{task.title}: {task.status}</div>
        ))}
        {agents.map((agent) => (
          <div key={agent.agentId}>{agent.description}: {agent.status}</div>
        ))}
        {toolCalls.map((tool) => (
          <div key={tool.callId}>{tool.tool}: {tool.status}</div>
        ))}
      </aside>
      <button type="button" onClick={() => sendMessage('Hello!')}>
        Send
      </button>
    </div>
  );
}

useAstraChat projects task-board snapshots and agent lifecycle events into stable tasks and agents arrays while retaining raw agentEvents for custom audit or visualization needs. Integration boundaries such as agentBinding, runtimeProfile, executionBudget, capabilities, explain, context, allowSkills, allowTools, workspaceBinding, and executorBinding are passed through the same typed chat request rather than being flattened into prompt text. workspaceBinding uses the canonical request shape (kind plus optional display_name, root, source, and authority); the workspace object observed in runtime events is a separate response shape.

§5.5 Edge protocol (from TypeScript)

When a local edge runs tools, use the same routes as astra-thin-client:

Method Client API Route
Tool result postToolResult(body, { edgeExecutorId }) POST /tools/result + X-Astra-Edge-Id
Approval postApprovalRespond(body) POST /approval/respond
Register edge registerEdge(body, { edgeTransportId }) POST /agents/edge
Heartbeat postEdgeHeartbeat(body, { edgeTransportId }) POST /agents/edge/heartbeat
Legacy task lease helpers getTaskLease, postTaskLeaseClaim / Release / Renew /tasks/{id}/lease/... (not registered by the current runtime)

Constants and path helpers are exported from @astra/sdk (for example PATH_CHAT_STREAM, joinApiPath, ASTRA_EDGE_ID_HEADER).

API reference (high level)

AstraClient

Method Description
register(username, password, options?) Register; optional email, displayName
login / logout / getMe Auth (logout posts refresh_token)
createSession / getSession / listSessions / deleteSession Sessions
updateSession / closeSession / resumeSession / cancelSession PUT / POST under /sessions/{id}
getSessionActivity GET /sessions/{id}/activity
getSessionAudit GET …/audit/summary → SessionAuditSummary
getSessionReflect / getSessionDecisionTrace GET /chat/session/{id}/reflect · …/decision-trace (optional query: focus, last_n, question)
createRun POST /chat (non-streaming run)
listRuns GET /runs?limit&after_updated_at&after_run_id → RunListResponse (runs normalized to RunStatus[], seek cursor preferred)
getRunStatus / cancelRun / pauseRun / resumeRun GET/DELETE/POST under /chat/runs/{id}
getRunEvents GET /chat/runs/{id}/stream?last_index= (buffered SSE parsed to StreamEvent[])
delegateRun / listDelegations / pauseDelegations / resumeDelegations Multi-agent: POST …/delegate, GET …/delegations, POST …/delegations/pause · resume
streamChat POST /chat/stream (SSE)
getSessionEvents / listEvents / getCausalChain GET /events/session/{id}, GET /events?…, GET /events/causal-chain/{id}
getEdgesStatus GET /edges/status
memoryStore / memorySearch / … /memory/*
listSkills GET /skills (maps skills[] to SkillInfo[])
§5.5 postToolResult, postApprovalRespond, registerEdge, postEdgeHeartbeat

AstraWebSocket

Method Description
connect() / close() Lifecycle
on / off Event subscription
sendMessage Chat message (session_id in payload when given)
approveToolCall Tool approval

Utilities

Export Description
parseSseDataEvents Parse full SSE text body into StreamEvent[]
buildQueryString Build ?a=1&b=2 from a params object (skips undefined/null)
PATH_*, joinApiPath, sessionClosePath, chatRunDelegatePath, eventsSessionPath, … Path constants and helpers aligned with Rust astra-thin-client

Types

Stream event types, ChatRequest, RunListResponse, SessionAuditSummary, SessionUpdateBody, SessionActivityResponse, ReflectReport, ReflectQueryParams, DelegationRequestBody, DelegationResponse, EventResponse, EventListResponse, EventListFilters, EdgeStatusResponse, §5.5 request bodies, and more are exported from @astra/sdk.

Testing

cd packages/sdk
npm ci
npm test
npm run build

Vitest config: vitest.config.ts. Suites cover AstraClient, SSE/WebSocket helpers, React hooks, and paths (buildQueryString, joinApiPath, path encoders).

License

Apache-2.0. See the repository's LICENSE.