@pouchy_ai/admin-sdk 0.26.1 → 0.27.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  All notable changes to `@pouchy_ai/admin-sdk` are documented here.
4
4
 
5
+ ## 0.27.1 — 2026-08-28
6
+
7
+ - **Docs-only, but read it if you use `encodingFormat: 'base64'`.** The 0.27.0
8
+ README's decode snippet was `new Float32Array(Buffer.from(s,'base64').buffer)`
9
+ — on Node runtimes that pool small buffers (every 768-dim vector qualifies),
10
+ `.buffer` is the whole shared pool and that form silently decodes ~2048
11
+ garbage-tailed elements. Our own live acceptance probe caught it on first
12
+ field run. Correct, everywhere:
13
+ `const buf = Buffer.from(s, 'base64'); new Float32Array(buf.buffer, buf.byteOffset, buf.length / 4)`.
14
+ No client code changed.
15
+
16
+ ## 0.27.0 — 2026-08-28
17
+
18
+ - **New: `embedTexts` — text → embedding vectors** (`POST
19
+ /v1/admin/utility/embeddings`), the vector sibling of `extractJson`:
20
+ stateless, no companion side effects, typed failures (`invalid_request` /
21
+ `unavailable` / `provider_error`). For a backend that ranks its own
22
+ inventory by cosine (album/asset pick, intent-gate exemplars) and wants one
23
+ billing/key channel instead of a second embedding vendor. Batch up to 64
24
+ texts per call; the response `model` is a **stable id** to persist next to
25
+ every stored vector and PIN in later requests — an id the deployment cannot
26
+ serve exactly answers 400, never a silent substitute, so a platform model
27
+ swap surfaces as an explicit reindex signal. `encodingFormat: 'base64'`
28
+ returns little-endian float32 base64 (~3.5× smaller) for bulk reindex.
29
+ Motivating ask: LuckyDramas' album selfie pick + L2 memory intent gate,
30
+ previously on a second vendor key at their BFF.
31
+
32
+ ## 0.26.2 — 2026-08-21
33
+
34
+ - **`createAgent` / `updateAgent` responses now type the additive
35
+ `droppedSkills?: string[]` field.** The platform filters `skills` slugs to
36
+ its registered allowlist; it now names anything it filtered instead of
37
+ returning a 200 whose `agent.skills` is silently missing what you sent.
38
+ Motivating field report: a manifest said `client_action`, the platform had
39
+ registered `client-action`, and the diff between request and response was
40
+ the only clue. Additive server field (older servers simply never set it) —
41
+ no behavioural change in this client.
42
+
5
43
  ## 0.26.1 — 2026-08-09
6
44
 
7
45
  - **Docs-only.** No type, method or signature moves. `extractJson`'s failure
package/README.md CHANGED
@@ -158,6 +158,62 @@ completion arrived but did not parse or satisfy the schema; the error carries
158
158
  `raw`, the text actually returned). Tokens roll into the project's month usage
159
159
  like any other model call.
160
160
 
161
+ ### Embedding vectors — use `embedTexts`, not a second vendor key
162
+
163
+ The vector sibling of `extractJson`: text → embedding vectors, stateless, no
164
+ companion side effects. For a backend that ranks its **own** inventory by cosine
165
+ (album/asset pick, memory intent-gate exemplars) and wants one billing/key
166
+ channel instead of holding a separate embedding vendor's key beside its Pouchy
167
+ keys — retrieval stays yours; Pouchy only turns text into vectors.
168
+
169
+ ```ts
170
+ const res = await admin.embedTexts({
171
+ input: ['Send me a casual home coffee selfie', '来张咖啡店自拍'] // ≤ 64 per call
172
+ });
173
+ // res.model → 'openai-text-embedding-3-small-768' (STABLE id — persist it)
174
+ // res.dimensions → 768 (== every vector's length)
175
+ // res.data[i] → { index: i, embedding: number[] } (index-aligned)
176
+ ```
177
+
178
+ Two rules keep a stored index honest:
179
+
180
+ 1. **Persist `res.model` next to every stored vector and refuse mixed-model
181
+ cosine.** Then **pin** it in later requests (`model:
182
+ 'openai-text-embedding-3-small-768'`): an id the deployment cannot serve
183
+ exactly answers `400` — never a silent substitute — so a platform model swap
184
+ surfaces on your side as an explicit reindex signal.
185
+ 2. **Texts are never silently truncated.** An over-cap text (> 8000 chars) is a
186
+ `400` naming its index; split or summarize it yourself, so what you embedded
187
+ is always exactly what you stored.
188
+
189
+ For bulk reindex, `encodingFormat: 'base64'` returns each vector as
190
+ little-endian float32 base64 (~3.5× smaller at 768 dims). Decode with the
191
+ **offset-safe** form:
192
+
193
+ ```ts
194
+ const res = await admin.embedTexts({
195
+ input: ['来张咖啡店自拍'],
196
+ encodingFormat: 'base64'
197
+ });
198
+ for (const { embedding } of res.data) {
199
+ const buf = Buffer.from(embedding as string, 'base64');
200
+ const vec = new Float32Array(buf.buffer, buf.byteOffset, buf.length / 4);
201
+ // vec.length === res.dimensions, always
202
+ void vec;
203
+ }
204
+ ```
205
+
206
+ Not `new Float32Array(buf.buffer)` — on Node runtimes that pool small buffers
207
+ (every 768-dim vector qualifies), `.buffer` is the whole shared pool and the
208
+ naive form silently decodes ~2048 garbage-tailed elements. Caught live by our
209
+ own acceptance probe; the three-arg form is correct everywhere.
210
+
211
+ Failures are typed: `invalid_request` (400 — fix and resend), `unavailable`
212
+ (503 — transient, back off and retry), `provider_error` (502 — the upstream
213
+ embedder rejected a request the platform believed valid; short provider message
214
+ included). Prompt tokens roll into the project's month usage (tagged
215
+ `utility_embed`); there is no separate platform credit rate.
216
+
161
217
  ### Spoken audio — use `synthesizeSpeech`, not `/call`
162
218
 
163
219
  The audio sibling of `extractJson`: text → a downloadable **mp3 file**, OUTSIDE
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const ADMIN_SDK_VERSION = "0.26.1";
1
+ export declare const ADMIN_SDK_VERSION = "0.27.1";
2
2
  export declare const DEFAULT_BASE_URL = "https://pouchy.ai/v1/admin";
3
3
  /** Deadline for the routes whose server handler declares `maxDuration: 300` —
4
4
  * the server's own ceiling plus headroom, so a client abort can only ever mean
@@ -59,7 +59,7 @@ export interface AdminClientOptions {
59
59
  * published: on this plane the status line never disambiguates, so two of the
60
60
  * durable-run routes answer 409 for two entirely different reasons each, with
61
61
  * different recoveries. `code` is the only thing that tells them apart. */
62
- export declare const ADMIN_ERROR_CODES: readonly ["schedule_limit_reached", "one_shot_spent", "ghost_row", "channel_limit_reached", "webhook_limit_reached", "run_limit_reached", "reembed_required", "run_terminal", "run_not_parked", "stale_token", "run_not_waiting", "event_mismatch"];
62
+ export declare const ADMIN_ERROR_CODES: readonly ["story_contract_v2_write_disabled", "story_contract_v3_write_disabled", "episode_history_incomplete", "episode_ended", "idempotency_key_reused", "stale_deliberation", "deliberation_disabled", "deliberation_uncalibrated", "deliberation_not_enabled", "deliberation_candidates_exceeded", "schedule_limit_reached", "one_shot_spent", "ghost_row", "channel_limit_reached", "webhook_limit_reached", "run_limit_reached", "reembed_required", "run_terminal", "run_not_parked", "stale_token", "run_not_waiting", "event_mismatch"];
63
63
  /** `AdminApiError.code` values. The `(string & {})` arm keeps the type open for
64
64
  * codes newer than this SDK build while preserving autocomplete — same
65
65
  * doctrine as the companion SDK's `CompanionErrorCodeValue`. */
@@ -444,18 +444,24 @@ export interface AdminClient {
444
444
  listAgents(): Promise<{
445
445
  agents: Agent[];
446
446
  }>;
447
+ /** `droppedSkills` (when present) names any `skills` slugs the platform's
448
+ * registered-skill allowlist filtered out — a slug typo (e.g. `client_action`
449
+ * vs the registered `client-action`) is named, never silently absent. */
447
450
  createAgent(input: {
448
451
  name: string;
449
452
  archetype: string;
450
453
  systemPrompt?: string;
451
454
  } & Record<string, unknown>): Promise<{
452
455
  agent: Agent;
456
+ droppedSkills?: string[];
453
457
  }>;
454
458
  getAgent(agentId: string): Promise<{
455
459
  agent: Agent;
456
460
  }>;
461
+ /** See createAgent — `droppedSkills` echoes allowlist-filtered slugs. */
457
462
  updateAgent(agentId: string, patch: Partial<Agent> & Record<string, unknown>): Promise<{
458
463
  agent: Agent;
464
+ droppedSkills?: string[];
459
465
  }>;
460
466
  deleteAgent(agentId: string): Promise<{
461
467
  deleted: boolean;
@@ -717,6 +723,52 @@ export interface AdminClient {
717
723
  completionTokens: number;
718
724
  };
719
725
  }>;
726
+ /** Text → embedding vectors, OUTSIDE the companion — the vector sibling of
727
+ * `extractJson`: stateless (no session, no persona, no memory), one provider
728
+ * call, typed failures. For a backend that ranks its OWN inventory by cosine
729
+ * (album/asset pick, intent-gate exemplars) and wants one billing/key channel
730
+ * instead of a second embedding vendor beside its Pouchy keys.
731
+ *
732
+ * `input` is one text or a batch (≤ 64 per request, each ≤ 8000 chars — an
733
+ * over-cap text is a 400 naming its index; texts are NEVER silently
734
+ * truncated, because a clipped vector poisons a stored index invisibly).
735
+ *
736
+ * The response `model` is a STABLE id (e.g.
737
+ * `openai-text-embedding-3-small-768`): persist it next to every stored
738
+ * vector, refuse mixed-model cosine, and PIN it in later requests — an id
739
+ * the deployment cannot serve exactly answers 400 rather than silently
740
+ * substituting, so a platform model swap surfaces as an explicit reindex
741
+ * signal. `dimensions` always equals every vector's length.
742
+ *
743
+ * `encodingFormat: 'base64'` returns each vector as little-endian float32
744
+ * base64 (~3.5× smaller at 768 dims — use for bulk reindex); default
745
+ * `'float'` returns `number[]`.
746
+ *
747
+ * Failures: `invalid_request` (400 — fix and resend), `unavailable` (503 —
748
+ * transient, back off and retry), `provider_error` (502 — upstream rejected
749
+ * a request the platform believed valid, short provider message included).
750
+ *
751
+ * Prompt tokens roll into the project's month usage (tagged
752
+ * `utility_embed`); no separate platform credit rate. */
753
+ embedTexts(input: {
754
+ input: string | string[];
755
+ model?: string;
756
+ dimensions?: number;
757
+ encodingFormat?: 'float' | 'base64';
758
+ }): Promise<{
759
+ object: 'list';
760
+ model: string;
761
+ dimensions: number;
762
+ data: Array<{
763
+ object: 'embedding';
764
+ index: number;
765
+ embedding: number[] | string;
766
+ }>;
767
+ usage?: {
768
+ promptTokens: number;
769
+ totalTokens: number;
770
+ };
771
+ }>;
720
772
  /** Text → a spoken-audio FILE, OUTSIDE the companion and OUTSIDE a realtime
721
773
  * call. The audio sibling of `extractJson`: stateless (no session, no
722
774
  * persona, no memory), one synthesis call, a downloadable clip back. Use it
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // import { createAdminClient } from '@pouchy_ai/admin-sdk';
9
9
  // const admin = createAdminClient({ adminKey: process.env.POUCHY_ADMIN_KEY! });
10
10
  // const { agents } = await admin.listAgents();
11
- export const ADMIN_SDK_VERSION = '0.26.1';
11
+ export const ADMIN_SDK_VERSION = '0.27.1';
12
12
  export const DEFAULT_BASE_URL = 'https://pouchy.ai/v1/admin';
13
13
  /** Default per-request timeout (ms). A hung upstream otherwise never rejects. */
14
14
  const DEFAULT_TIMEOUT_MS = 30_000;
@@ -70,6 +70,16 @@ export function requestDeadlineMs(method, path) {
70
70
  * durable-run routes answer 409 for two entirely different reasons each, with
71
71
  * different recoveries. `code` is the only thing that tells them apart. */
72
72
  export const ADMIN_ERROR_CODES = [
73
+ 'story_contract_v2_write_disabled',
74
+ 'story_contract_v3_write_disabled',
75
+ 'episode_history_incomplete',
76
+ 'episode_ended',
77
+ 'idempotency_key_reused',
78
+ 'stale_deliberation',
79
+ 'deliberation_disabled',
80
+ 'deliberation_uncalibrated',
81
+ 'deliberation_not_enabled',
82
+ 'deliberation_candidates_exceeded',
73
83
  'schedule_limit_reached',
74
84
  'one_shot_spent',
75
85
  'ghost_row',
@@ -250,6 +260,7 @@ export function createAdminClient(opts) {
250
260
  ingestKnowledgeUrl: (input) => request('POST', '/knowledge/url', input),
251
261
  searchKnowledge: (query) => request('POST', '/knowledge/search', { query }),
252
262
  extractJson: (input) => request('POST', '/utility/json', input),
263
+ embedTexts: (input) => request('POST', '/utility/embeddings', input),
253
264
  synthesizeSpeech: (input) => request('POST', '/utility/tts', input),
254
265
  deleteKnowledge: (id) => request('DELETE', `/knowledge/${encodeURIComponent(id)}`),
255
266
  listSkills: () => request('GET', '/skills'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/admin-sdk",
3
- "version": "0.26.1",
3
+ "version": "0.27.1",
4
4
  "description": "Typed TypeScript client for the Pouchy Admin API \u2014 manage agents, keys, end users, knowledge, skills, channels, schedules, webhooks and credentials headlessly, with a project Admin key.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",