Skip to content
  • sdk
  • cli
  • api

Upgrading to samva 0.37 and the CLI 0.31

samva 0.36 and 0.37 replace send modes with decision rules, delete templates permanently, and drop the RSA verifier. Upgrade the CLI and template toolchain.

Upgrade samva to 0.37.0, @samva/webhooks to 0.37.0, and @samva/cli to 0.31.0. Start with decision rules: send modes, per-grant approval flags, and mailbox policies are gone, and a policy write now names the version it replaces. Then move template deletion calls and commands, pass the review round when you approve or deny a held action, upgrade a template project's own authoring packages, and check for an import of the webhook protocol entrypoint. Each package's full notes are on its GitHub release.

What you need to do

  1. Decision rules replace send modes (0.36, CLI 0.31). A mailbox action now records a decision: allow, hold, or refuse, with its decider, reason, and the scopes that produced it. Rules live on one scope, read and written at GET, PUT, and DELETE /v1/policies/{kind}/{id}, where kind is organization, mailbox, apiKey, or agent. Each rule sets either a decide default or an atLeast floor; a narrower scope replaces a broader default, and every matching floor still applies. A read returns version, and every replacement or clear must pass the observed ifVersion. A stale one returns ConflictError (409), so reload the snapshot, review it, and write again instead of retrying an old configuration. Clearing a scope advances the version too, so an earlier replacement cannot restore rules you removed. Policy changes need a signed-in person or an OAuth session; an API key cannot write them. Removed with the old model: mailbox grant and API key sendMode, the mailbox policy operations under /v1/mailboxes/{id}/policies, and sendMode on an extension installation, which is now policy. See configure decision rules.
import { createClient } from "samva";

const samva = createClient({
  authToken: process.env.SAMVA_OAUTH_TOKEN!,
  orgSlug: process.env.SAMVA_ORG_SLUG!,
});

const current = await samva.policies.getPolicy({ kind: "mailbox", id: "mbx_..." });

await samva.policies.setPolicy({
  kind: "mailbox",
  id: "mbx_...",
  ifVersion: current.version,
  presets: [],
  rules: [{ match: { effect: "write" }, atLeast: "person" }],
});

The CLI adds samva policies get, samva policies set, and samva policies clear, each taking a scope kind and id. Pass --if-version with a version you already read; without it, the command reads the current version first and writes that.

  1. Approvals need the review round (0.36, CLI 0.31). An approval or denial must carry decision.reviewRound from the action you reviewed. Read the action first and pass that number: a stale round returns ConflictError (409), because the action changed since you read it. Approval also takes edits.text or edits.html to send reviewed copy, and always: true to allow matching future actions from the same actor while policy floors and guards still apply. Naming only one email body clears the other. The CLI requires --review-round on samva mailboxes actions approve and deny, adds --edit-text to approval, and no longer accepts --send-mode on samva api-keys create or samva api-keys update. Needs you returns heldAction items, tool calls included, and counts them as heldActions where it reported heldSends; each item carries its decision, and a held tool call also carries its tool. Pending-approval webhook events carry the same decision.
import { createClient } from "samva";

const samva = createClient({
  authToken: process.env.SAMVA_OAUTH_TOKEN!,
  orgSlug: process.env.SAMVA_ORG_SLUG!,
});
const mailboxId = "mbx_7q2xk9mvt4znw8rh";
const actionId = "mbxact_7q2xk9mvt4znw8rh";

const action = await samva.mailboxes.getAction({ id: mailboxId, actionId });
if (action.decision === null) throw new Error("This action has no decision to review");

await samva.mailboxes.approveAction({
  id: mailboxId,
  actionId,
  reviewRound: action.decision.reviewRound,
});
  1. Template deletion is permanent (0.36, CLI 0.31). templates.delete replaces templates.remove and removes a template with its publications, and templates.deleteProject removes a project with every template in it. Both keep sent-message history and its snapshots, and both refuse a template that something still uses with TemplateInUseError (409) naming each reference by kind and id. isActive is gone from template responses and from create and update payloads, so a template either exists or it does not. The CLI renames samva templates remove to samva templates delete <id>, previews with --dry-run, and confirms with --yes; samva templates projects delete <id> removes a project and every template in it. See delete a template.

  2. Namespace keys reach scheduled sends and reports (0.36, CLI 0.31). A namespace key can schedule a message, read analytics, and read conversations in its own namespace, and a scheduled send keeps that namespace when it fires. With an organization credential, pass namespaceId to select one; scheduled messages and conversations return theirs. A namespace key also reads the organization's brands. On the CLI, --namespace replaces --namespace-id on topics and newsletters commands, and the scheduled-message, analytics, conversation, mailbox, webhook endpoint, and API key commands take --namespace. See namespaces.

  3. A template project upgrades its authoring packages (CLI 0.31). samva templates dev, check, render, snapshot, build, and export load @samva/markup and @samva/vite from the project rather than from the CLI, and 0.31 drives the 0.23 release line. A project still on 0.22.x stops with the version it found and the command that fixes it, so upgrade both packages in the project:

npm install --save-dev @samva/markup@~0.23.0 @samva/vite@~0.23.0

See author templates locally.

  1. Webhook verification is HMAC only (0.37). @samva/webhooks 0.37.0 removes verifyWebhookAtWithPublicKeys, WebhookSignatureVersion, webhookSignedContent, and encodeWebhookSignature from the @samva/webhooks/protocol entrypoint. Samva signs deliveries with HMAC v1 and never sent the RSA v2 scheme those exports verified, so a handler that used them has nothing to check. Verify with verify or verifyRequest and your endpoint's signing secret; replay checks, payload decoding, and the Promise and Effect adapters are unchanged. See verify each request.
import { verifyRequest } from "@samva/webhooks";

export async function handle(request: Request): Promise<{ id: string; type: string }> {
  const { id, event } = await verifyRequest(request, process.env.SAMVA_WEBHOOK_SECRET!);
  return { id, type: event.type };
}

Preview · Available to selected organizations.

Agents. An agent definition no longer takes mode, and an integration grant no longer takes requireApproval. Both are part of definition.policy, the same presets, rules, and instructions a policy scope holds, and the published policy decides every proposed action. A model rule has Samva judge the action and holds it for a person when judging is unavailable. A reply is a write action, so mode: "supervised" becomes a write rule at person; a final rule alone leaves replies to the model default and they can send without a person. Knowledge sources must belong to the agent's namespace: attach organization-level sources only to an organization-level agent, or the draft is refused with ValidationError (422) and definition.knowledgeSourceIds naming each source. An agent can call hand_to_team to hand a conversation to staff with a held handoff decision. Approving with always writes an agent policy scope that you can read and clear, and a saved test now expects the decisions for its actions in expectedDecisions instead of approvedReply. See what needs you.

// Before: the whole definition carried its supervision mode.
await samva.agents.replaceDraft({ id: agentId, revision, definition: { ...definition, mode: "supervised" } });

// After: the same definition carries its decision rules. A reply is a write action, and a final
// rule alone would leave replies to the model default.
await samva.agents.replaceDraft({
  id: agentId,
  revision,
  definition: {
    ...definition,
    policy: {
      presets: [],
      rules: [
        { match: { effect: "write" }, atLeast: "person" },
        { match: { effect: "final" }, atLeast: "person" },
      ],
    },
  },
});

Also new

  • Search conversations by meaning (0.36). mailboxes.listThreads and listOrganizationThreads take q and rank the best 100 matches across subjects, participants, message text, and attachment filenames. Filters narrow that set without changing its order, and new mail takes a few seconds to become searchable. See search conversations.
  • Per-namespace sending health (0.36). Each namespace sends through its own email sending setup, so a pause in one namespace leaves the others sending. email.sending.paused and email.sending.resumed carry namespaceId, or null for organization-level sending. See email sending events.
  • Mailbox sending readiness (0.36). An email endpoint reports sendingStatus as pending, ready, or failed. Creation returns while the address is still being set up, so pass an Idempotency-Key to retry safely, including with samva mailboxes create --idempotency-key. See sending readiness.
  • A complete sign-in link (CLI 0.31). samva login prints the link that carries the code and asks you to confirm the page shows the same code. A dropped connection or a temporary server error while waiting for approval keeps waiting until the code expires.
  • Template editor keys (CLI 0.31). In samva templates dev, ⌘S or Ctrl+S saves a version, ⌘ or Ctrl with +, -, or 0 zooms the canvas, and Escape closes an open panel. Keys match the physical key, and on macOS only ⌘ saves or zooms.

Links

Related

All changelog entries