Package Exports
- @phrony/sdk
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@phrony/sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@phrony/sdk
TypeScript client for the Phrony public API. It wraps fetch, sends X-API-Key on every request, and provides typed responses for agents (start runs, list sessions), runs (status, conversation, follow-up messages, SSE stream), and the file library (presign, upload, finalize).
Auth models: this package is built for workspace API keys (phk_…) against /v1/.... Workspace access tokens (pwt_…) authenticate Authorization: Bearer on internal gateway routes (different URLs and scope model). Use the Phrony CLI and dashboard access tokens for CI on internal APIs; do not substitute an API key where a Bearer access token is required.
Requirements: Node 18+ (or any runtime with fetch).
Source: github.com/phrony-platform/ts-sdk (MIT).
Install
pnpm add @phrony/sdkCreate a client
Set apiKey to a Phrony API key (phk_...). It must be scoped to the same agent and trigger as the routes you call.
baseUrl(optional) defaults tohttps://api.phrony.com. Your team’s host may differ; use the base URL from the agent’s Access tab in the Phrony dashboard.fetch(optional) defaults toglobalThis.fetch. On older Node or custom stacks, pass an implementation (for example fromundici).
import { Phrony } from "@phrony/sdk";
const phrony = new Phrony({
apiKey: process.env.PHRONY_API_KEY!,
// baseUrl: "https://api.phrony.com", // default
});Start a run and poll until done
startRun calls POST /v1/agents/{agentId}/runs. The body’s input must match your deployed agent’s input contract; omit it or use {} when the agent does not need static input.
getRun calls GET /v1/runs/{runId}. Keep polling (or use the SSE stream below) until status is in a state you consider finished. Exact status strings depend on your workspace; see the Phrony Runs API reference.
import { Phrony } from "@phrony/sdk";
const phrony = new Phrony({ apiKey: "phk_…" });
const agentId = "00000000-0000-0000-0000-000000000000";
const { runId } = await phrony.startRun(agentId, {
input: { question: "Summarize the attached policy." },
});
const interval = 1_000;
let run = await phrony.getRun(runId);
while (shouldKeepPolling(run.status)) {
await new Promise((r) => setTimeout(r, interval));
run = await phrony.getRun(runId);
}
console.log(run.output);
function shouldKeepPolling(status: string) {
// Example only — align with your agent’s real lifecycle.
return status === "Running" || status.startsWith("Waiting");
}List sessions
listSessions calls GET /v1/agents/{agentId}/sessions with optional skip, take, versionId, and status (exact session status).
const page = await phrony.listSessions(agentId, { skip: 0, take: 20 });
console.log(page.items, page.total);Read the conversation
getConversation calls GET /v1/runs/{runId}/conversation. Use stepContent: "slim" to strip redundant keys from object step content values (per the API: session_id, run_id, tenant_id, agent_id, event_id where applicable).
The response is typed as ConversationResponse:
session—ConversationSession(id, status,input,startContextwith frozen version limits anduserInput, tokens, and timing fields).runs—ConversationRunSummary[](every run in the session, including sub-runs, withinput,output, and trigger metadata).items—ConversationTimelineItem[](merged session timeline:ordinalfor global sort,sequenceper run,typefor the step, andcontentwhose shape depends on the step).
Narrow items[].content with type and the optional content aliases (ToolCallStepContent, ReasoningStepContent, RunCompletedItemContent, SessionCompletedItemContent, and so on). Agent-specific payloads under session.input or userInput stay JsonValue.
import {
type ConversationResponse,
type ConversationTimelineItem,
Phrony,
} from "@phrony/sdk";
const phrony = new Phrony({ apiKey: "phk_…" });
const convo: ConversationResponse = await phrony.getConversation(runId, {
stepContent: "slim",
});
const toolCalls: ConversationTimelineItem[] = convo.items.filter(
(i) => i.type === "ToolCall",
);Send a follow-up (messages or HITL)
sendRunMessage calls POST /v1/runs/{runId}/messages (or …/input with { path: "input" }). The JSON must include runId (the client sets it for you) plus either userTaskId for human-in-the-loop, or text / message, and/or files, as required for your run’s pause. See the Phrony Runs API for the full field list.
await phrony.sendRunMessage(
runId,
{ text: "Use the 2024 figures only." },
);For a user task (approval, option pick, and so on), add fields like userTaskId, approved, or selectedOptionId in the same object.
Skip a phrony_wait timer
When polling shows WaitingForTimer (the agent used phrony_wait and the scheduled time has not passed), skipAgentWaitTimer calls POST /v1/runs/{runId}/skip-agent-wait. The response is 202 Accepted with the same JSON shape as sendRunMessage (typically { "status": "Running" }). The actor recorded for audit is the API key used on the request.
await phrony.skipAgentWaitTimer(runId);Subscribe to the run stream (SSE)
streamRunEvents calls GET /v1/runs/{runId}/stream and parses Server-Sent Events so each data line becomes one JSON object (StreamEvent with ts, kind, subject, data). Use this from a server or from Node; browser EventSource cannot set X-API-Key.
import type { Phrony } from "@phrony/sdk";
async function followStream(phrony: Phrony, runId: string) {
for await (const ev of phrony.streamRunEvents(runId)) {
console.log(ev.kind, ev.subject, ev.data);
}
}To parse a Response body yourself (for example a custom fetch pipeline), use the exported parseSSEStream(res.body).
File uploads (file library)
The API key must have Allow file uploads enabled. The flow is: presign → PUT bytes to uploadUrl with every header from requiredHeaders → finalize → pass a file reference in input or in files on a run message.
uploadWorkspaceFile runs presign, upload, and finalize in one call and returns leafName (the last segment of objectKey) for use in a phronyFile reference.
import { fileRefFromObjectKey, Phrony } from "@phrony/sdk";
import { readFile } from "node:fs/promises";
const phrony = new Phrony({ apiKey: "phk_…" });
const buf = await readFile("document.pdf");
const { leafName, finalize } = await phrony.uploadWorkspaceFile({
filename: "document.pdf",
mediaType: "application/pdf",
data: buf,
});
// Match your agent’s input field name, e.g. "document"
await phrony.startRun(agentId, {
input: {
document: fileRefFromObjectKey(finalize.objectKey, "application/pdf"),
},
});fileRefFromObjectKey builds { phronyFile: true, filename, mediaType } using the leaf file name, as required for run input. For message attachments, use the files[] shape with phronyFile: true and objectKey (see the Runs API).
Errors
Non-success HTTP status codes throw PhronyAPIError with status, path, and the response body as text (often JSON from the server).
import { Phrony, PhronyAPIError } from "@phrony/sdk";
try {
await phrony.getRun("…");
} catch (e) {
if (e instanceof PhronyAPIError) {
console.error(e.status, e.path, e.message, e.body);
}
throw e;
}API map
| Client method | HTTP |
|---|---|
startRun |
POST /v1/agents/{agentId}/runs |
listSessions |
GET /v1/agents/{agentId}/sessions |
getRun |
GET /v1/runs/{runId} |
getConversation |
GET /v1/runs/{runId}/conversation |
sendRunMessage |
POST /v1/runs/{runId}/messages or …/input |
streamRunEvents |
GET /v1/runs/{runId}/stream (SSE) |
presignFile / finalizeFile |
POST /v1/file-library/presign / …/finalize |
uploadWorkspaceFile |
presign + PUT + finalize |
Documentation
For request and response field details, authentication scoping, and product behavior, use the Phrony API documentation (Agents, Runs, File library, Authentication).