Wardian docs

Claude inside your app

A suite part that declares claude:sample can ask Claude a question while it runs. Wardian answers through the Claude provider set up in Settings: the Anthropic API or Amazon Bedrock. The app never sees a key.

The example meeting-notes turns raw notes into a summary, decisions and action items, and still works when no provider is set up.

Ask#

{ "name": "ai", "caps": ["claude:sample"], "provides": ["extract"] }
const sample = await ctx.cap('sample');      // null when no provider is set up
if (!sample) return localFallback(notes);

const { text, truncated } = await sample('Summarize these notes in three sentences:\n\n' + notes);

const items = await sample.json(
  'Return JSON {"actions":[{"owner":string,"task":string}]} for these notes:\n\n' + notes,
  { modelTier: 'quick' }
);
CallResolves to
sample(prompt, opts){ text, truncated }
sample.json(prompt, opts)the parsed JSON
OptionDoes
signalan AbortSignal; aborting rejects the call with cancelled
modelTier'quick' for the faster model; otherwise the main model
onTextcalled once with { text } when the answer arrives (the answer does not stream)

Always handle null#

ctx.cap('sample') resolves to null when the host has no provider. An app with claude:sample must still work then. Offer a plain fallback, and say how to turn AI on:

if (!sample) {
  note.textContent = 'AI is off. To turn it on, open Settings → Claude.';
  return extractByRules(notes);
}

Handle every error#

Errors carry e.code:

CodeMeansDo
not_grantedthe user did not allow this packagesay so; offer the fallback
rate_limitedtoo many requestswait, then let the user try again
refusedClaude declinedshow the reason; do not retry the same prompt
invalid_jsonsample.json got text that is not JSONretry once with a firmer prompt, or fall back
prompt_too_largethe prompt is over the limitask the user to shorten the input
over_budgetthis app used its tokens for todaysay so; offer the fallback until tomorrow (UTC)
cancelledyour signal aborteddo nothing
erroranything elseshow the message

You are asked first#

The first time a package uses claude:sample, Wardian asks you, the same way it asks about channels. Settings → App permissions lists the answer, and you can revoke it. A host never grants a capability that the part's contract in suite.json does not list.

It runs in the background#

A request to Claude can take a while. Wardian runs it as a background job on the server (ADR-2610072118), so no browser connection waits on it, and leaving the app does not stop it. The Jobs button lists it while it runs. Show <wardian-progress> while you wait, with cancelable wired to an AbortController:

const stop = new AbortController();
bar.start('Reading your notes');
bar.addEventListener('cancel', () => stop.abort(), { once: true });
try {
  const out = await sample.json(prompt, { signal: stop.signal });
  bar.done('Done');
} catch (e) {
  bar.fail(e.code === 'cancelled' ? 'Stopped' : 'Failed');
}

Cost#

Each call uses credit on the key or the AWS account set up in Settings. Use modelTier: 'quick' for small jobs: classification, extraction, short summaries.

Wardian counts each app's tokens per UTC day. Past its daily cap, 200,000 tokens unless an admin set another in Settings → Usage, every call fails with over_budget until the next day. An admin also chooses the models and the most tokens one answer may have (Keys and Claude settings).

This page is docs/site/ai.md in the repository. Something wrong or missing? Change that file.