npm package: @opined/sdk
Description: JavaScript SDK for Opined API
Latest version: 0.2.0

# Opined JS SDK

Install:

```bash
npm install @opined/sdk
```

## Runtime quickstart (project-scoped)

```ts
import { OpinedRuntimeClient, OpinedAdminClient } from "@opined/sdk";

const runtime = new OpinedRuntimeClient(process.env.OPINED_BASE_URL!, process.env.OPINED_API_KEY!);
const admin = new OpinedAdminClient("http://localhost:8080", "your_admin_api_key");

const response = await runtime.chatCompletions({
  agent_id: "agt_assistant",
  plan_key: "starter",
  customer_ref: {
    external_id: "customer_123",
    name: "Jane Doe",
  },
  messages: [{ role: "user", content: "Hello" }],
});
```

## Multimodal + prompt variables

```ts
const multimodal = await runtime.chatCompletions({
  mode: "standard",
  agent_id: "agt_assistant",
  plan_key: "starter",
  customer_ref: { external_id: "customer_123" },
  variables: {
    customer_name: "Maria",
    account_tier: "pro",
  },
  messages: [
    {
      role: "user",
      parts: [
        runtime.textPart("Describe this image"),
        runtime.imagePartFromURL("https://example.com/image.jpg", "image/jpeg")
      ]
    }
  ]
});
```

Notes:
- `variables` is optional.
- Missing non-required placeholders are rendered as empty strings.
- Only variables marked as `required` in template schema should fail validation.

## Tool-calling loop (runtime + your backend tools)

```ts
const templated = await runtime.chatCompletions({
  mode: "standard",
  agent_id: "agt_assistant",
  plan_key: "starter",
  customer_ref: { external_id: "customer_123" },
  variables: {
    customer_name: "Maria",
    account_tier: "pro",
    dynamic_context: "última fatura atrasada"
  },
  tools: ["agenda.create", "patient.lookup"],
  messages: [{ role: "user", content: "Quais os próximos passos?" }],
});

const firstChoice = templated.choices[0];
if (firstChoice.message.tool_calls?.length) {
  // Execute tool in your backend, then send tool output back in a new request.
}
```

## Admin examples

```ts
const quickstart = await admin.onboardingQuickstart(
  {
    org_id: "org_123",
    workspace_id: "ws_123",
    mode: "quick_start",
    preset: "simple_default",
    agent_name: "Support Copilot"
  },
  "idem-quickstart-001"
);

const discovery = await admin.onboardingDiscovery("org_123", "ws_123");
const onboardingStatus = await admin.onboardingStatus("org_123", "ws_123");

await admin.upsertCanonicalMapping({
  org_id: "org_123",
  workspace_id: "ws_123",
  external_type: "external_customer_id",
  external_key: "crm_0001",
  opined_id: "cus_000001",
  metadata: { source: "crm" }
});

await admin.upsertPlanCommercialConfig({
  org_id: "org_123",
  plan_id: "pln_123",
  billing_mode: "subscription_overage",
  llm_key_mode: "managed",
  currency: "USD",
  monthly_base_usd: 49,
  included_cost_usd: 15,
  overage_per_1k_token_usd: 0.003,
  markup_percent: 18
});

const invoice = await admin.invoicePreview("org_123", "sub_123", "pln_456");
const summary = await admin.billingSummary("org_123", "sub_123");
const paymentStatus = await admin.paymentSetupRequired("org_123", "sub_123");
```

## Compatibility note

Some endpoints and SDK helpers still support richer context (`org_id`, `workspace_id`, `product_key`) for multi-tenant backend flows. For most project-scoped runtime integrations, start with:

- `agent_id`
- `plan_key`
- `customer_ref`
- `messages`
- optional `variables`

Validation behavior:
- Missing non-required placeholders render as empty strings.
- Missing required variables still return `variable_validation_failed`.

## Tools starter utilities (TypeScript)

The SDK now includes helper layers to make tool execution plug-and-play:

- `createToolRegistry(...)`
- `withTimeout(...)`
- `withRetries(...)`
- `executeToolCalls(...)`
- `parseToolArguments(...)`

Behavior notes:

- `runWithTools(...)` orchestrates the structured `tool_calls` / `tool_results` loop for you.
- If the loop reaches `maxSteps`, the SDK now returns a controlled assistant response with `meta.tool_loop.max_steps_exceeded = true` instead of throwing a hard error.
- For best tool-calling reliability, register tools in Opined with specific `input_schema` fields instead of a single generic free-text argument whenever possible.

Example:

```ts
import {
  OpinedRuntimeClient,
  createToolRegistry,
  withTimeout,
  withRetries,
} from "@opined/sdk";

const runtime = new OpinedRuntimeClient(process.env.OPINED_BASE_URL!, process.env.OPINED_API_KEY!);

const registry = createToolRegistry({
  "agenda.create": async (args) => {
    // your SaaS business logic
    const appointment = await createAppointment(args);
    return { output: appointment };
  },
  "patient.lookup": async (args) => {
    const patient = await findPatient(args);
    return patient; // auto-normalized as output
  },
});

// Compose resilience layers
const executeTool = withRetries(withTimeout(registry, 5000), {
  maxRetries: 2,
  retryDelayMS: 150,
});

const response = await runtime.runWithTools({
  context: {
    org_id: "org_123",
    workspace_id: "ws_123",
    plan_key: "starter",
    product_key: "liz-ai",
    customer_ref: { external_id: "clinic_001" },
    agent_id: "agt_123",
    environment: "production",
  },
  message: "Agende retorno para amanhã às 10h",
  variables: {
    doctor_name: "Dra. Ana",
    clinic_name: "Clínica Vida",
    timezone: "America/Sao_Paulo",
  },
  tools: ["agenda.create", "patient.lookup"],
  executeTool,
});
```

Recommended layering for SaaS runtimes:

1. Registry (`createToolRegistry`) to map names to business handlers.
2. Timeout guard (`withTimeout`) to avoid hanging tool calls.
3. Retry policy (`withRetries`) for transient failures.
4. Runtime loop (`runWithTools`) to orchestrate `tool_calls` + `tool_results`.