@pouchy_ai/admin-sdk 0.16.1 → 0.18.0

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,91 @@
2
2
 
3
3
  All notable changes to `@pouchy_ai/admin-sdk` are documented here.
4
4
 
5
+ ## 0.18.0 — 2026-08-07
6
+
7
+ - **`extractJson` takes `reasoningEffort`, and its server default moved to
8
+ `'low'`.** 0.17.0 shipped the endpoint running on the shared one-shot floor of
9
+ `'minimal'`, and nothing could change it — the request accepted a `model` but
10
+ not a thinking budget, so an integrator who noticed the quality had no lever
11
+ short of swapping models.
12
+
13
+ It is measured, not theoretical. An A/B over 26 utterances against an
14
+ integrator's own extractor agreed on kind for all 22 inputs carrying a SINGLE
15
+ directive, and diverged on exactly the two carrying more than one:
16
+ 「以后别叫我宝贝,叫我老板」 came back labelled by its leading clause (a
17
+ boundary) instead of its operative one (a nickname), and 「叫我阿凯,别剧透,
18
+ 我不喜欢虐心结局」 merged three directives into two. In both the summary text
19
+ was correct and complete — only the segmentation collapsed, and in both the
20
+ earlier-appearing kind absorbed the later one. That is the signature of a
21
+ budget that can write one good sentence but cannot deliberate over how many
22
+ items there are, which is exactly what an extraction endpoint is for.
23
+
24
+ `'minimal'` restores 0.17.0's behaviour if you want the cheapest, fastest
25
+ extraction; raise it further when one input can carry several directives at
26
+ once. `AdminReasoningEffort` is exported. Additive — every 0.17.0 call
27
+ compiles unchanged — but note the DEFAULT moved, so extraction calls that pass
28
+ nothing now think one notch harder and cost slightly more.
29
+
30
+ A domain precedence rule ("a nickname wins over a boundary when the user says
31
+ what to call them") still belongs in YOUR schema's `description`, not in this
32
+ endpoint: it forwards your schema verbatim and holds no opinion about your
33
+ vocabulary.
34
+
35
+ ## 0.17.0 — 2026-08-07
36
+
37
+ - **New `extractJson()` — structured JSON output, outside the companion.**
38
+ `POST /v1/admin/utility/json`. Additive; nothing else changed.
39
+
40
+ This exists because using a companion agent for extraction does not work, and
41
+ did not fail in a way that explained itself. A companion turn is a
42
+ conversation engine — persona prompt, memory recall, tools — and when the
43
+ model returns an empty completion it answers with a natural-language fallback
44
+ line rather than shipping an empty bubble. Correct for a chat surface, wrong
45
+ for a parser. An extraction prompt reliably *reaches* that fallback, because
46
+ the reasoning-effort bump off the cheapest tier is gated on conversational and
47
+ functional cues (weather, search, wallet, social) that an extraction input
48
+ never matches. The result was a soft chat sentence where JSON was expected.
49
+
50
+ `extractJson()` shares none of that machinery: no persona, no memory, no
51
+ tools, no session, no fallback. One provider call with `response_format` set,
52
+ and a parsed result.
53
+
54
+ ```ts
55
+ const { data } = await admin.extractJson<{ nickname: string | null }>({
56
+ content: '叫我 Alex',
57
+ schema: {
58
+ type: 'object',
59
+ additionalProperties: false,
60
+ required: ['nickname'],
61
+ properties: { nickname: { type: ['string', 'null'] } }
62
+ }
63
+ });
64
+ // → { nickname: 'Alex' }
65
+ ```
66
+
67
+ - **`strict` defaults to true**, which asks the provider to ENFORCE the schema.
68
+ The schema must then sit inside the provider's structured-output subset: root
69
+ object, `additionalProperties: false`, and every property listed in
70
+ `required` — model an optional field as a union with `null` rather than by
71
+ omitting it from `required`. A schema outside the subset comes back as `400`
72
+ / `code: 'schema_invalid'` carrying the provider's own reason instead of
73
+ failing at generation time. Pass `strict: false` to fall back to plain JSON
74
+ mode plus server-side validation.
75
+
76
+ - **Failures are typed, not prose.** `schema_invalid` (400 — fix the schema;
77
+ retrying verbatim cannot help), `unavailable` (5xx — transient, back off),
78
+ `invalid_json` (502 — a completion arrived but did not parse or did not
79
+ satisfy the schema). `invalid_json` carries `raw`, the text actually
80
+ returned, so the failure is debuggable rather than opaque.
81
+
82
+ - **Note on schema key names.** The companion reply path strips objects
83
+ matching internal memory-pipeline signatures, among them
84
+ `{domain, key, value, label}` and `{content, kind, importance}`. That
85
+ stripping does not apply to this endpoint — but if you were previously
86
+ extracting through a companion agent with a schema shaped like either of
87
+ those, a *correct* answer could be removed before you saw it. Another reason
88
+ to move extraction here.
89
+
5
90
  ## 0.16.1 — 2026-08-06
6
91
 
7
92
  Documentation only — no type, signature or runtime change. Two doc comments
package/README.md CHANGED
@@ -58,6 +58,101 @@ console.log(`armed — ${armed.reprovisioned} running instance(s) updated`);
58
58
  // The agent can now drive the API from the skill's prose via http_request.
59
59
  ```
60
60
 
61
+ ### Structured JSON — use `extractJson`, not a companion agent
62
+
63
+ ```ts
64
+ const { data } = await admin.extractJson<{ nickname: string | null }>({
65
+ content: '叫我 Alex',
66
+ schema: {
67
+ type: 'object',
68
+ additionalProperties: false,
69
+ required: ['nickname'], // strict mode: EVERY property, always
70
+ properties: { nickname: { type: ['string', 'null'] } } // optional → union with null
71
+ }
72
+ });
73
+ console.log(data.nickname); // 'Alex'
74
+ ```
75
+
76
+ A companion turn is a **conversation** engine — persona prompt, memory recall,
77
+ tools — and when the model returns an empty completion it answers with a
78
+ natural-language fallback line rather than an empty bubble. That is right for a
79
+ chat surface and wrong for a parser, and an extraction prompt reliably reaches
80
+ it, because the reasoning-effort bump off the cheapest tier is gated on
81
+ conversational/functional cues an extraction input never matches. So a companion
82
+ agent asked for JSON returns chat filler, fairly consistently.
83
+
84
+ `extractJson` shares none of that: no persona, no memory, no tools, no session,
85
+ no fallback — one provider call with `response_format` set. `strict` defaults to
86
+ true, which makes the provider **enforce** the schema; the schema must then sit
87
+ inside the provider's structured-output subset (root object,
88
+ `additionalProperties: false`, every property listed in `required`). Pass
89
+ `strict: false` for schemas outside it — you still get JSON mode plus
90
+ server-side validation.
91
+
92
+ #### `reasoningEffort` — matters most when one input carries several directives
93
+
94
+ `reasoningEffort` (`'minimal' | 'low' | 'medium' | 'high'`, default `'low'`) is
95
+ the thinking budget. It is worth a moment because extraction fails at the cheap
96
+ tier in a specific, easy-to-miss way: **the summary comes out right and the
97
+ segmentation collapses.**
98
+
99
+ Measured over 26 utterances against an integrator's own extractor, the previous
100
+ floor (`'minimal'`) agreed on kind for all 22 inputs carrying a SINGLE
101
+ directive, then diverged on both inputs carrying more than one — 「以后别叫我宝
102
+ 贝,叫我老板」 labelled by its leading clause (a boundary) rather than its
103
+ operative one (a nickname), and 「叫我阿凯,别剧透,我不喜欢虐心结局」 merged
104
+ three directives into two. In each case the item's own text mentioned every
105
+ clause; only the count and the label were wrong, which is exactly the failure a
106
+ schema check cannot catch.
107
+
108
+ Pass `'minimal'` for the cheapest, fastest extraction when each input carries at
109
+ most one directive. Raise it above the default when the split matters more than
110
+ the latency.
111
+
112
+ Precedence between YOUR kinds — "a nickname wins over a boundary when the user
113
+ also says what to call them" — belongs in the schema's `description`, not here:
114
+ this endpoint forwards your schema verbatim and holds no opinion about your
115
+ vocabulary.
116
+
117
+ ```ts
118
+ const { data } = await admin.extractJson<{ items: { kind: string; summary: string }[] }>({
119
+ content: '以后别叫我宝贝,叫我老板',
120
+ reasoningEffort: 'low',
121
+ schema: {
122
+ type: 'object',
123
+ additionalProperties: false,
124
+ required: ['items'],
125
+ properties: {
126
+ items: {
127
+ type: 'array',
128
+ items: {
129
+ type: 'object',
130
+ additionalProperties: false,
131
+ required: ['kind', 'summary'],
132
+ properties: {
133
+ kind: {
134
+ type: 'string',
135
+ enum: ['nickname', 'boundary', 'preference'],
136
+ // The precedence rule lives HERE, in your vocabulary's own words.
137
+ description:
138
+ 'One directive per item — never merge two of different kinds. If the user says what to CALL them (even alongside what not to), the item is `nickname` and the rejected form goes in the summary; `boundary` is only for a prohibition with no replacement.'
139
+ },
140
+ summary: { type: 'string' }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ });
147
+ ```
148
+
149
+ Failures are typed rather than prose, so the recoveries are distinguishable:
150
+ `schema_invalid` (400 — fix the schema; retrying verbatim cannot help),
151
+ `unavailable` (5xx — transient, back off), and `invalid_json` (502 — a
152
+ completion arrived but did not parse or satisfy the schema; the error carries
153
+ `raw`, the text actually returned). Tokens roll into the project's month usage
154
+ like any other model call.
155
+
61
156
  Every skill knob (`setSkillRate`, `setSkillDailyCap`, `grantSkill`) returns
62
157
  `SkillKnobResult<T>` — the knob you set plus `reprovisioned` (instances the new
63
158
  def reached) and `truncated`. A knob only binds a running agent once the def
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const ADMIN_SDK_VERSION = "0.16.1";
1
+ export declare const ADMIN_SDK_VERSION = "0.18.0";
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
@@ -436,6 +436,10 @@ export type SkillKnobResult<T> = T & {
436
436
  reprovisioned: number;
437
437
  truncated: boolean;
438
438
  };
439
+ /** Thinking budget for `extractJson`, cheapest first. Mirrors the server's
440
+ * vocabulary; an unknown value is refused with a 400 by the endpoint rather
441
+ * than forwarded to the provider. */
442
+ export type AdminReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
439
443
  export interface AdminClient {
440
444
  listAgents(): Promise<{
441
445
  agents: Agent[];
@@ -652,6 +656,61 @@ export interface AdminClient {
652
656
  mode: 'semantic' | 'lexical';
653
657
  hits: unknown[];
654
658
  }>;
659
+ /** Structured JSON extraction, OUTSIDE the companion.
660
+ *
661
+ * Use this — not a companion agent — whenever you want machine-readable
662
+ * output. A companion turn is a conversation engine: it carries a persona
663
+ * prompt, recalls memory, and when the model returns nothing it answers with
664
+ * a natural-language fallback line rather than dead air. That is right for a
665
+ * chat bubble and wrong for a parser, and an extraction prompt reliably
666
+ * triggers it, because the reasoning-effort bump off the cheapest tier is
667
+ * gated on conversational/functional cues that an extraction input does not
668
+ * match. This endpoint shares none of that: no persona, no memory, no tools,
669
+ * no session, no fallback — one provider call with `response_format` set.
670
+ *
671
+ * `strict` (default true) asks the provider to ENFORCE the schema. It must
672
+ * then sit inside the provider's structured-output subset: a root object,
673
+ * `additionalProperties: false`, and EVERY property listed in `required`
674
+ * (model an optional field as a union with `null`, not by omitting it from
675
+ * `required`). A schema outside the subset is rejected with `400` /
676
+ * `code: 'schema_invalid'` naming the provider's reason — pass
677
+ * `strict: false` to fall back to plain JSON mode plus server-side
678
+ * validation.
679
+ *
680
+ * Failures are typed rather than prose, which is the point:
681
+ * `schema_invalid` (400 — fix the schema, retrying verbatim cannot help),
682
+ * `unavailable` (5xx — transient, back off), `invalid_json` (502 — a
683
+ * completion arrived but did not parse or did not satisfy the schema; the
684
+ * error carries `raw`, the text actually returned).
685
+ *
686
+ * `reasoningEffort` is the thinking budget, default `'low'`. It matters more
687
+ * than it sounds for extraction: the shared one-shot floor is `'minimal'`,
688
+ * and an A/B over 26 utterances found that floor agreed on kind for all 22
689
+ * inputs carrying a SINGLE directive, then diverged on both inputs carrying
690
+ * more than one — labelling "以后别叫我宝贝,叫我老板" by its leading clause
691
+ * (a boundary) rather than its operative one (a nickname), and merging three
692
+ * directives into two. The summary text was correct both times; only the
693
+ * segmentation collapsed. Raise it when one input can carry several
694
+ * directives at once and the split matters more than the latency; pass
695
+ * `'minimal'` for the cheapest, fastest extraction.
696
+ *
697
+ * Tokens roll into the project's month usage like any other model call. */
698
+ extractJson<T = unknown>(input: {
699
+ schema: Record<string, unknown>;
700
+ content: string;
701
+ system?: string;
702
+ strict?: boolean;
703
+ model?: string;
704
+ schemaName?: string;
705
+ reasoningEffort?: AdminReasoningEffort;
706
+ }): Promise<{
707
+ data: T;
708
+ raw: string;
709
+ usage?: {
710
+ promptTokens: number;
711
+ completionTokens: number;
712
+ };
713
+ }>;
655
714
  deleteKnowledge(docId: string): Promise<{
656
715
  deleted: boolean;
657
716
  }>;
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.16.1';
11
+ export const ADMIN_SDK_VERSION = '0.18.0';
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;
@@ -247,6 +247,7 @@ export function createAdminClient(opts) {
247
247
  ingestKnowledgeFile: (input) => request('POST', '/knowledge/file', input),
248
248
  ingestKnowledgeUrl: (input) => request('POST', '/knowledge/url', input),
249
249
  searchKnowledge: (query) => request('POST', '/knowledge/search', { query }),
250
+ extractJson: (input) => request('POST', '/utility/json', input),
250
251
  deleteKnowledge: (id) => request('DELETE', `/knowledge/${encodeURIComponent(id)}`),
251
252
  listSkills: () => request('GET', '/skills'),
252
253
  installSkill: (input) => request('POST', '/skills', input),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pouchy_ai/admin-sdk",
3
- "version": "0.16.1",
3
+ "version": "0.18.0",
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",