@pinecall/skills 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pinecall/skills",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Agent Skills for the Pinecall SDK — installable into Claude Code, Antigravity, Cursor, Copilot and any agent that supports the open Skills format.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -69,6 +69,40 @@ The response shape:
69
69
 
70
70
  Tokens are single-use, scoped to the agent, and expire in 60 seconds. See [Security](/security) for the full security model.
71
71
 
72
+ ### Sealed session metadata (trusted context)
73
+
74
+ Your token endpoint already knows who the user is — it's behind your auth. Bake that
75
+ identity into the token by passing a metadata object as the last argument to
76
+ `createToken`. It's **sealed into the signed token on your server**, so the browser
77
+ cannot forge or change it:
78
+
79
+ ```typescript
80
+ app.get("/api/token", authMiddleware, async (req, res) => {
81
+ const token = await mara.createToken("webrtc", {
82
+ userId: req.user.id,
83
+ plan: req.user.plan,
84
+ tenantId: req.user.orgId,
85
+ });
86
+ res.json(token);
87
+ });
88
+ ```
89
+
90
+ It surfaces — trusted — as `call.metadata` in your agent:
91
+
92
+ ```typescript
93
+ agent.on("call.started", (call) => {
94
+ console.log(call.metadata.userId, call.metadata.plan); // straight from the sealed token
95
+ });
96
+ ```
97
+
98
+ The same applies to **chat** tokens — mint with `createToken("chat", { ... })` and read
99
+ `call.metadata` in the chat session. (With the `Pinecall` client instead of an `Agent`
100
+ instance, it's `pc.createToken("webrtc", "mara", { ... })`.)
101
+
102
+ > **Trusted vs client-supplied.** The widget also accepts a `metadata` prop set in the
103
+ > browser — handy, but a user can forge it. For anything you'll act on (identity, plan,
104
+ > entitlements, tenant), seal it in the token here instead of trusting the client prop.
105
+
72
106
  ## 3. Drop in the widget
73
107
 
74
108
  ```bash
@@ -21,7 +21,7 @@ call.from // "+13186330963" or "sip:..."
21
21
  call.to // destination number / URI
22
22
  call.direction // "inbound" | "outbound"
23
23
  call.transport // "phone" | "webrtc" | "chat" | "whatsapp" | "unknown"
24
- call.metadata // custom metadata from the channel or dial()
24
+ call.metadata // sealed token metadata (createToken), dial() metadata, or channel context
25
25
  call.transcript // [{ role: "user", content: "..." }, ...] — user + assistant only
26
26
  call.messages // full LLM history (populated on call.ended)
27
27
  call.currentBotText // live preview of what the bot is saying (accumulated bot.word events)
@@ -31,6 +31,22 @@ call.endedAt // epoch seconds
31
31
  call.reason // "hangup" | "timeout" | ...
32
32
  ```
33
33
 
34
+ ### `metadata`
35
+
36
+ Arbitrary context attached to the session, available in `call.started` and throughout the call. It comes from one of:
37
+
38
+ - **Sealed token metadata** (browser WebRTC / chat) — passed to [`createToken(channel, agentId, metadata)`](/api/pinecall) on your server and sealed into the signed token. **Trusted**: the browser can't forge it, so it's safe for identity / plan / tenant.
39
+ - **`dial()` metadata** (outbound calls) — passed when you place the call.
40
+ - **Channel context** — provider-supplied fields for the transport.
41
+
42
+ ```typescript
43
+ agent.on("call.started", (call) => {
44
+ if (call.metadata?.plan === "pro") enablePremiumTools(call);
45
+ });
46
+ ```
47
+
48
+ The client-supplied `metadata` prop on the browser widget / `VoiceSession` also lands here, but is set in the browser — don't trust it for authorization; seal that in the token instead.
49
+
34
50
  ### `currentBotText`
35
51
 
36
52
  A live preview of what the bot is currently saying. Built automatically from `bot.word` events — grows word-by-word as TTS plays, resets on each new `bot.speaking`, clears after `bot.finished` or `bot.interrupted`.
@@ -132,15 +132,27 @@ Unregister an agent. Returns `boolean` indicating whether the agent existed.
132
132
  const removed = pc.removeAgent("mara");
133
133
  ```
134
134
 
135
- ### `createToken(channel, agentId)`
135
+ ### `createToken(channel, agentId, metadata?)`
136
136
 
137
- Generate a short-lived, single-use token for browser WebRTC or chat connections. Used to mint tokens for browsers.
137
+ Generate a short-lived, single-use token for browser **WebRTC** or **chat** connections. Used to mint tokens for browsers.
138
138
 
139
139
  ```typescript
140
140
  const token = await pc.createToken("webrtc", "mara");
141
141
  // { token, server, expiresIn }
142
142
  ```
143
143
 
144
+ **Sealed session metadata** — pass a third argument to bake trusted context into the token:
145
+
146
+ ```typescript
147
+ const token = await pc.createToken("chat", "mara", { userId: "u_123", plan: "pro" });
148
+ ```
149
+
150
+ The metadata is **sealed into the signed token on your server**, so the browser cannot forge or alter it. It surfaces as [`call.metadata`](/api/call) in your `call.started` handler — use it for per-user / multi-tenant context you can trust (auth identity, plan, tenant id). Works identically for `"webrtc"` and `"chat"`.
151
+
152
+ > With an `Agent` instance, use `agent.createToken(channel, metadata?)` (the `agentId` is implicit).
153
+
154
+ > ⚠️ This is **not** the client-supplied `metadata` prop on the widget / `VoiceSession` — that is set in the browser and can be forged. For anything used in authorization, seal it in the token here.
155
+
144
156
  See [Security](/security) for the full token model.
145
157
 
146
158
  ### `stream(res?, options?)`