dsh-plugin-subscription-oauth 0.6.0 → 0.6.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.
@@ -95,7 +95,15 @@ export declare class KimiAdapter extends LlmAdapter {
95
95
  private discovered;
96
96
  /** Ids the user added by hand, cached so the sync catalog can name them. */
97
97
  private userAddedIds;
98
+ /** Tools already reported as flattened, so each is stated once. */
99
+ private readonly flattenedTools;
98
100
  constructor(options: KimiAdapterOptions);
101
+ /**
102
+ * Tool schemas in the shape Moonshot accepts: no `$ref`, and under its size
103
+ * ceiling. A declaration too large to send keeps its arguments, and that is
104
+ * reported once per tool rather than on every turn.
105
+ */
106
+ private moonshotTools;
99
107
  providerInfo(provider: string): LlmProviderInfo;
100
108
  providerRetryPolicy(): undefined;
101
109
  listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
@@ -14,7 +14,7 @@ import { idleWatchdog, httpLlmError, isMissingOrInvalidCredential, mapFetchFailu
14
14
  import { streamProviderResponses } from './responses-stream.js';
15
15
  import { fallbackEntry, findEntry, resolveCatalog } from './catalog.js';
16
16
  import { streamResponses, toResponsesInput, toResponsesTools } from '../translate/responses.js';
17
- import { moonshotToolParameters } from '../translate/moonshot-schema.js';
17
+ import { prepareMoonshotTool } from '../translate/moonshot-schema.js';
18
18
  /**
19
19
  * Coerce one decoded credential entry into a Kimi session.
20
20
  * @param value - decoded JSON value for one account.
@@ -131,10 +131,31 @@ export class KimiAdapter extends LlmAdapter {
131
131
  discovered;
132
132
  /** Ids the user added by hand, cached so the sync catalog can name them. */
133
133
  userAddedIds = [];
134
+ /** Tools already reported as flattened, so each is stated once. */
135
+ flattenedTools = new Set();
134
136
  constructor(options) {
135
137
  super();
136
138
  this.options = options;
137
139
  }
140
+ /**
141
+ * Tool schemas in the shape Moonshot accepts: no `$ref`, and under its size
142
+ * ceiling. A declaration too large to send keeps its arguments, and that is
143
+ * reported once per tool rather than on every turn.
144
+ */
145
+ moonshotTools(tools) {
146
+ return tools.map((tool) => {
147
+ const prepared = prepareMoonshotTool(tool.parameters);
148
+ if (prepared.preparedAs !== 'as-declared' && !this.flattenedTools.has(tool.name)) {
149
+ this.flattenedTools.add(tool.name);
150
+ this.options.onWarn?.(prepared.preparedAs === 'flattened'
151
+ ? `kimi: ${tool.name} declares an argument schema larger than the endpoint accepts; `
152
+ + 'its arguments are described without their nested structure'
153
+ : `kimi: ${tool.name} declares an argument schema larger than the endpoint accepts; `
154
+ + 'it is offered as an unrestricted object');
155
+ }
156
+ return { ...tool, parameters: prepared.parameters };
157
+ });
158
+ }
138
159
  providerInfo(provider) {
139
160
  return { id: provider, name: KIMI_PROVIDER_NAME };
140
161
  }
@@ -311,7 +332,11 @@ export class KimiAdapter extends LlmAdapter {
311
332
  input: resolved.input,
312
333
  ...options.tools === undefined || options.tools.length === 0
313
334
  ? {}
314
- : { tools: toResponsesTools(options.tools, moonshotToolParameters), tool_choice: 'auto', parallel_tool_calls: true },
335
+ : {
336
+ tools: toResponsesTools(this.moonshotTools(options.tools)),
337
+ tool_choice: 'auto',
338
+ parallel_tool_calls: true,
339
+ },
315
340
  ...options.maxTokens === undefined ? {} : { max_output_tokens: options.maxTokens },
316
341
  ...options.reasoningEffort === undefined
317
342
  ? {}
@@ -1,31 +1,54 @@
1
1
  /**
2
2
  * Tool-schema preparation for the Moonshot wire (Kimi Code).
3
3
  *
4
- * Moonshot validates `tools[].function.parameters` with its own JSON-Schema
5
- * reader, and that reader cannot settle a `$ref` that carries sibling
6
- * keywords: instead of intersecting the two, it re-expands the node, so the
7
- * whole request is refused with
8
- * `detected infinite recursion without termination condition`. The harness
9
- * forwards tool schemas exactly as their owner declared them, and the MCP
10
- * tools REA registers use that shape heavily —
11
- * `{"$ref": "#/$defs/__schema12", "description": "..."}` — 153 of 170 tools
12
- * carried at least one such node, and the first one Moonshot reached killed
13
- * every Kimi request that included a tool.
4
+ * Moonshot validates `tools[].function.parameters` with its own reader, and
5
+ * that reader expands every `$ref` before it measures or checks the schema. The
6
+ * harness forwards each tool's schema exactly as its owner declared it, and
7
+ * every MCP tool REA registers holds its shapes in a `$defs` block that its
8
+ * properties reference, so Kimi refused the whole request with
9
+ * `tools.function.parameters is not a valid moonshot flavored json schema`.
14
10
  *
15
- * A `$ref` that stands alone is accepted, and so is keeping `$defs` for it, so
16
- * only the offending nodes are rewritten: the referenced schema is inlined
17
- * under the node's own keywords (JSON Schema 2020-12 applies both, and the
18
- * node's own keywords win on a conflict), which leaves the wire schema with
19
- * standalone references only. Resolution is bounded by the reference stack, so
20
- * a real cycle ends by dropping the `$ref` and keeping the sibling keywords
21
- * rather than recursing.
11
+ * Live probes against `api.kimi.com` measured both edges of that reader:
12
+ *
13
+ * - a `$ref` whose target itself holds `$ref`s is refused as
14
+ * `detected infinite recursion without termination condition` — the failing
15
+ * tool reported the node `properties.actions` and, once that node was
16
+ * inlined so its reference stood alone, the deeper `properties.actions.items`.
17
+ * A one-level reference is accepted; a chain is not.
18
+ * - the expanded schema has a size ceiling between 80,081 B (accepted) and
19
+ * 124,389 B (refused). `compare_web_captures` declares 57,175 B of shared
20
+ * definitions but expands past 153,000 B, so it was refused even as declared.
21
+ *
22
+ * So this module sends no references at all — every local pointer is replaced
23
+ * by the definition it names, and the document keywords that only existed to
24
+ * hold them (`$defs`, `$schema`, `$id`, `definitions`) are dropped. A pointer
25
+ * that re-enters itself cannot be inlined, and there the referencing node keeps
26
+ * its own keywords instead of recursing. A tool whose expanded schema still
27
+ * exceeds the ceiling is flattened in steps — the arguments with their prose,
28
+ * then the arguments alone, then an unrestricted object — so that one oversized
29
+ * tool costs its own detail rather than the whole request.
22
30
  *
23
31
  * @module dsh-plugin-subscription-oauth/translate/moonshot-schema
24
32
  */
33
+ /** How much of a declaration a schema too large to send had to give up. */
34
+ export type MoonshotPreparation =
35
+ /** Sent as declared, with its references inlined. */
36
+ 'as-declared'
37
+ /** Only the arguments: names, types, choices, `required`, and prose while it fits. */
38
+ | 'flattened'
39
+ /** An unrestricted object, because even the arguments did not fit. */
40
+ | 'permissive';
41
+ /** One prepared tool schema together with what had to be done to it. */
42
+ export interface MoonshotToolSchema {
43
+ /** The schema to send. */
44
+ parameters: Record<string, unknown>;
45
+ /** What the declaration had to give up to fit. */
46
+ preparedAs: MoonshotPreparation;
47
+ }
25
48
  /**
26
49
  * Prepare one tool's parameters for Moonshot.
27
50
  *
28
51
  * @param parameters - the tool's declared argument schema.
29
- * @returns a detached schema whose `$ref` nodes carry no sibling keywords.
52
+ * @returns the schema to send and what it had to give up to fit the endpoint.
30
53
  */
31
- export declare function moonshotToolParameters(parameters: Record<string, unknown>): Record<string, unknown>;
54
+ export declare function prepareMoonshotTool(parameters: Record<string, unknown>): MoonshotToolSchema;
@@ -1,27 +1,47 @@
1
1
  /**
2
2
  * Tool-schema preparation for the Moonshot wire (Kimi Code).
3
3
  *
4
- * Moonshot validates `tools[].function.parameters` with its own JSON-Schema
5
- * reader, and that reader cannot settle a `$ref` that carries sibling
6
- * keywords: instead of intersecting the two, it re-expands the node, so the
7
- * whole request is refused with
8
- * `detected infinite recursion without termination condition`. The harness
9
- * forwards tool schemas exactly as their owner declared them, and the MCP
10
- * tools REA registers use that shape heavily —
11
- * `{"$ref": "#/$defs/__schema12", "description": "..."}` — 153 of 170 tools
12
- * carried at least one such node, and the first one Moonshot reached killed
13
- * every Kimi request that included a tool.
4
+ * Moonshot validates `tools[].function.parameters` with its own reader, and
5
+ * that reader expands every `$ref` before it measures or checks the schema. The
6
+ * harness forwards each tool's schema exactly as its owner declared it, and
7
+ * every MCP tool REA registers holds its shapes in a `$defs` block that its
8
+ * properties reference, so Kimi refused the whole request with
9
+ * `tools.function.parameters is not a valid moonshot flavored json schema`.
14
10
  *
15
- * A `$ref` that stands alone is accepted, and so is keeping `$defs` for it, so
16
- * only the offending nodes are rewritten: the referenced schema is inlined
17
- * under the node's own keywords (JSON Schema 2020-12 applies both, and the
18
- * node's own keywords win on a conflict), which leaves the wire schema with
19
- * standalone references only. Resolution is bounded by the reference stack, so
20
- * a real cycle ends by dropping the `$ref` and keeping the sibling keywords
21
- * rather than recursing.
11
+ * Live probes against `api.kimi.com` measured both edges of that reader:
12
+ *
13
+ * - a `$ref` whose target itself holds `$ref`s is refused as
14
+ * `detected infinite recursion without termination condition` — the failing
15
+ * tool reported the node `properties.actions` and, once that node was
16
+ * inlined so its reference stood alone, the deeper `properties.actions.items`.
17
+ * A one-level reference is accepted; a chain is not.
18
+ * - the expanded schema has a size ceiling between 80,081 B (accepted) and
19
+ * 124,389 B (refused). `compare_web_captures` declares 57,175 B of shared
20
+ * definitions but expands past 153,000 B, so it was refused even as declared.
21
+ *
22
+ * So this module sends no references at all — every local pointer is replaced
23
+ * by the definition it names, and the document keywords that only existed to
24
+ * hold them (`$defs`, `$schema`, `$id`, `definitions`) are dropped. A pointer
25
+ * that re-enters itself cannot be inlined, and there the referencing node keeps
26
+ * its own keywords instead of recursing. A tool whose expanded schema still
27
+ * exceeds the ceiling is flattened in steps — the arguments with their prose,
28
+ * then the arguments alone, then an unrestricted object — so that one oversized
29
+ * tool costs its own detail rather than the whole request.
22
30
  *
23
31
  * @module dsh-plugin-subscription-oauth/translate/moonshot-schema
24
32
  */
33
+ /** Keywords that describe the schema document rather than the values it accepts. */
34
+ const DOCUMENT_KEYS = new Set(['$schema', '$id', '$defs', 'definitions']);
35
+ /** Root keywords a flattened schema keeps. */
36
+ const ROOT_KEYS = ['type', 'description', 'required', 'additionalProperties', 'minProperties', 'maxProperties'];
37
+ /**
38
+ * Largest expanded schema the endpoint is asked to take, in bytes.
39
+ *
40
+ * Measured rather than assumed: 80,081 B was accepted and 124,389 B was refused
41
+ * with `At path 'root': schema exceeds maximum allowed size`. This sits below
42
+ * both so a schema this plugin sends has room to spare.
43
+ */
44
+ const SIZE_BUDGET = 64 * 1024;
25
45
  /** True for a plain object usable as a schema node. */
26
46
  function object(value) {
27
47
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -43,7 +63,7 @@ function pointed(root, ref) {
43
63
  return node;
44
64
  }
45
65
  /**
46
- * Rewrite one node so every `$ref` left on the wire stands alone.
66
+ * Replace every reference reachable from one node with what it names.
47
67
  *
48
68
  * @param node - the node to rewrite.
49
69
  * @param root - the tool's root schema, which local pointers resolve against.
@@ -56,26 +76,67 @@ function inline(node, root, refs) {
56
76
  if (!object(node))
57
77
  return node;
58
78
  const { $ref, ...siblings } = node;
59
- if (typeof $ref !== 'string') {
60
- return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, inline(value, root, refs)]));
79
+ if (typeof $ref === 'string') {
80
+ // A repeated pointer, or one that names nothing local, cannot be inlined:
81
+ // the node's own keywords are all that is left.
82
+ const target = refs.has($ref) ? undefined : pointed(root, $ref);
83
+ return object(target)
84
+ ? inline({ ...target, ...siblings }, root, new Set([...refs, $ref]))
85
+ : inline(siblings, root, refs);
61
86
  }
62
- // A standalone reference is exactly what Moonshot expects, so it stays as is
63
- // and keeps its `$defs` entry.
64
- if (Object.keys(siblings).length === 0)
65
- return { $ref };
66
- // A repeated pointer, or one that is not a local object, cannot be inlined:
67
- // the sibling keywords are all that is left to terminate on.
68
- const target = refs.has($ref) ? undefined : pointed(root, $ref);
69
- return object(target)
70
- ? inline({ ...target, ...siblings }, root, new Set([...refs, $ref]))
71
- : inline(siblings, root, refs);
87
+ return Object.fromEntries(Object.entries(node)
88
+ .filter(([key]) => !DOCUMENT_KEYS.has(key))
89
+ .map(([key, value]) => [key, inline(value, root, refs)]));
90
+ }
91
+ /**
92
+ * Keep only what a model needs to call the tool: the root's own keywords, and
93
+ * each argument's name, type, value choices and — while the budget allows —
94
+ * its description.
95
+ *
96
+ * @param schema - an already inlined schema.
97
+ * @param prose - whether descriptions are worth their bytes.
98
+ * @returns the flattened schema.
99
+ */
100
+ function flatten(schema, prose) {
101
+ const out = {};
102
+ for (const key of ROOT_KEYS) {
103
+ if (Object.hasOwn(schema, key) && (prose || key !== 'description'))
104
+ out[key] = schema[key];
105
+ }
106
+ if (!object(schema.properties))
107
+ return out;
108
+ out.properties = Object.fromEntries(Object.entries(schema.properties).map(([name, property]) => {
109
+ const node = object(property) ? property : {};
110
+ const reduced = {};
111
+ if (typeof node.type === 'string')
112
+ reduced.type = node.type;
113
+ if (Array.isArray(node.enum))
114
+ reduced.enum = node.enum;
115
+ if (prose && typeof node.description === 'string')
116
+ reduced.description = node.description;
117
+ return [name, reduced];
118
+ }));
119
+ return out;
120
+ }
121
+ /** Bytes the endpoint measures once it has expanded every reference. */
122
+ function expandedBytes(schema) {
123
+ return Buffer.byteLength(JSON.stringify(schema));
72
124
  }
73
125
  /**
74
126
  * Prepare one tool's parameters for Moonshot.
75
127
  *
76
128
  * @param parameters - the tool's declared argument schema.
77
- * @returns a detached schema whose `$ref` nodes carry no sibling keywords.
129
+ * @returns the schema to send and what it had to give up to fit the endpoint.
78
130
  */
79
- export function moonshotToolParameters(parameters) {
80
- return inline(parameters, parameters, new Set());
131
+ export function prepareMoonshotTool(parameters) {
132
+ const inlined = inline(parameters, parameters, new Set());
133
+ if (expandedBytes(inlined) <= SIZE_BUDGET)
134
+ return { parameters: inlined, preparedAs: 'as-declared' };
135
+ const documented = flatten(inlined, true);
136
+ if (expandedBytes(documented) <= SIZE_BUDGET)
137
+ return { parameters: documented, preparedAs: 'flattened' };
138
+ const bare = flatten(inlined, false);
139
+ if (expandedBytes(bare) <= SIZE_BUDGET)
140
+ return { parameters: bare, preparedAs: 'flattened' };
141
+ return { parameters: { type: 'object', additionalProperties: true }, preparedAs: 'permissive' };
81
142
  }
@@ -53,11 +53,9 @@ export declare function toResponsesInput(messages: readonly RequestMessage[], sy
53
53
  * than letting the endpoint normalize every schema into strict mode.
54
54
  *
55
55
  * @param tools - the request's tool schemas.
56
- * @param prepare - an endpoint-specific rewrite of one parameters schema, for
57
- * a route whose JSON-Schema reader refuses the schema as declared.
58
56
  * @returns `tools` array entries.
59
57
  */
60
- export declare function toResponsesTools(tools: readonly ToolSchema[], prepare?: (parameters: Record<string, unknown>) => Record<string, unknown>): Record<string, unknown>[];
58
+ export declare function toResponsesTools(tools: readonly ToolSchema[]): Record<string, unknown>[];
61
59
  /** The `usage` object of a completed response. */
62
60
  export interface ResponsesUsage {
63
61
  input_tokens?: number;
@@ -124,16 +124,14 @@ export async function toResponsesInput(messages, system, resolveImage) {
124
124
  * than letting the endpoint normalize every schema into strict mode.
125
125
  *
126
126
  * @param tools - the request's tool schemas.
127
- * @param prepare - an endpoint-specific rewrite of one parameters schema, for
128
- * a route whose JSON-Schema reader refuses the schema as declared.
129
127
  * @returns `tools` array entries.
130
128
  */
131
- export function toResponsesTools(tools, prepare) {
129
+ export function toResponsesTools(tools) {
132
130
  return tools.map((tool) => ({
133
131
  type: 'function',
134
132
  name: tool.name,
135
133
  description: tool.description,
136
- parameters: prepare === undefined ? tool.parameters : prepare(tool.parameters),
134
+ parameters: tool.parameters,
137
135
  strict: false,
138
136
  }));
139
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-subscription-oauth",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Use subscription accounts as DeepSeek Harness LLM providers — Kimi Code (OAuth device flow), Grok / X Premium (OIDC + PKCE), Google Antigravity (Google OAuth + Cloud Code Assist) and GLM / BigModel (ZCode's browser sign-in) — with per-model visibility, drag-to-reorder Auto fallback and plan quotas from the web Settings page",
5
5
  "license": "MIT",
6
6
  "author": "EricFL1998",