@mengruo/dsh-vision-toolkit 0.0.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.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +356 -0
  4. package/README.zh.md +354 -0
  5. package/assets/community-group-qr.png +0 -0
  6. package/assets/dsh-conversation-artifact.png +0 -0
  7. package/assets/dsh-conversation-image-qa-top.png +0 -0
  8. package/assets/dsh-conversation-image-qa.png +0 -0
  9. package/assets/dsh-conversation-pixel-diff.png +0 -0
  10. package/assets/dsh-conversation-screenshot-debugging-top.png +0 -0
  11. package/assets/dsh-conversation-screenshot-debugging.png +0 -0
  12. package/assets/dsh-conversation-tool-call.png +0 -0
  13. package/assets/dsh-conversation-vision-trace.png +0 -0
  14. package/assets/dsh-view-example.png +0 -0
  15. package/assets/hero-v2.png +0 -0
  16. package/assets/logo_aihubmix.png +0 -0
  17. package/assets/python-bootstrap.json +48 -0
  18. package/assets/skill/SKILL.md +329 -0
  19. package/assets/skill/UPSTREAM.json +71 -0
  20. package/assets/skill/references/gui.md +88 -0
  21. package/assets/skill/references/long-screenshot-ocr.md +77 -0
  22. package/assets/skill/references/restore-graphic.md +84 -0
  23. package/assets/skill/references/restore-structure.md +45 -0
  24. package/assets/skill/references/restore-ui.md +202 -0
  25. package/assets/social-preview.png +0 -0
  26. package/assets/upstream/README.md +18 -0
  27. package/assets/upstream/focus-hint-comparison-1.webp +0 -0
  28. package/assets/upstream/focus-hint-comparison-2.webp +0 -0
  29. package/assets/upstream/infographic-reference.webp +0 -0
  30. package/assets/upstream/infographic-result.webp +0 -0
  31. package/assets/upstream/ui-fast-restore-reference.webp +0 -0
  32. package/assets/upstream/ui-fast-restore-result.webp +0 -0
  33. package/assets/upstream/ui-result.webp +0 -0
  34. package/assets/upstream/ui-sketch.webp +0 -0
  35. package/assets/vision-model-test.png +0 -0
  36. package/assets/vision-settings.png +0 -0
  37. package/assets/wechat-reward.png +0 -0
  38. package/cordis.patch.yml +6 -0
  39. package/docs/aihubmix-gemini-vision.i18n.yaml +6 -0
  40. package/docs/aihubmix-gemini-vision.md +138 -0
  41. package/docs/aihubmix-gemini-vision.zh.md +138 -0
  42. package/docs/assets/aihubmix-api-keys.png +0 -0
  43. package/docs/assets/aihubmix-create-key.png +0 -0
  44. package/docs/assets/aihubmix-free-vision-model.png +0 -0
  45. package/docs/assets/aihubmix-home.png +0 -0
  46. package/docs/assets/aihubmix-sign-up.png +0 -0
  47. package/docs/assets/groq-console-home.png +0 -0
  48. package/docs/assets/groq-console-keys-login.png +0 -0
  49. package/docs/assets/groq-docs-quickstart.png +0 -0
  50. package/docs/assets/groq-docs-qwen3.6.png +0 -0
  51. package/docs/assets/groq-docs-vision.png +0 -0
  52. package/docs/assets/vision-settings.png +0 -0
  53. package/docs/dsh-desktop-install.md +67 -0
  54. package/docs/dsh-desktop-install.zh.md +67 -0
  55. package/docs/groq-qwen3.6-vision.i18n.yaml +6 -0
  56. package/docs/groq-qwen3.6-vision.md +236 -0
  57. package/docs/groq-qwen3.6-vision.zh.md +236 -0
  58. package/docs/python-runtime.i18n.yaml +6 -0
  59. package/docs/python-runtime.md +89 -0
  60. package/docs/python-runtime.zh.md +89 -0
  61. package/docs/requirements-traceability/README.i18n.yaml +6 -0
  62. package/docs/requirements-traceability/README.md +75 -0
  63. package/docs/requirements-traceability/README.zh.md +75 -0
  64. package/examples/ui-restoration/README.i18n.yaml +6 -0
  65. package/examples/ui-restoration/README.md +70 -0
  66. package/examples/ui-restoration/README.zh.md +70 -0
  67. package/examples/ui-restoration/assets/final-heatmap.png +0 -0
  68. package/examples/ui-restoration/assets/final-report.json +83 -0
  69. package/examples/ui-restoration/assets/implementation.png +0 -0
  70. package/examples/ui-restoration/assets/initial-heatmap.png +0 -0
  71. package/examples/ui-restoration/assets/initial-report.json +83 -0
  72. package/examples/ui-restoration/assets/initial.png +0 -0
  73. package/examples/ui-restoration/assets/metrics.json +12 -0
  74. package/examples/ui-restoration/assets/reference.png +0 -0
  75. package/examples/ui-restoration/implementation.html +94 -0
  76. package/examples/ui-restoration/initial.html +57 -0
  77. package/lib/artifact-access.js +369 -0
  78. package/lib/artifact-access.js.map +1 -0
  79. package/lib/artifacts.js +56 -0
  80. package/lib/artifacts.js.map +1 -0
  81. package/lib/client.js +2099 -0
  82. package/lib/client.js.map +1 -0
  83. package/lib/config.js +172 -0
  84. package/lib/config.js.map +1 -0
  85. package/lib/defaults.js +6 -0
  86. package/lib/defaults.js.map +1 -0
  87. package/lib/errors.js +56 -0
  88. package/lib/errors.js.map +1 -0
  89. package/lib/evidence-cache.js +350 -0
  90. package/lib/evidence-cache.js.map +1 -0
  91. package/lib/exposure.js +247 -0
  92. package/lib/exposure.js.map +1 -0
  93. package/lib/image-input-variants.js +882 -0
  94. package/lib/image-input-variants.js.map +1 -0
  95. package/lib/index.js +105 -0
  96. package/lib/index.js.map +1 -0
  97. package/lib/paste-images.js +217 -0
  98. package/lib/paste-images.js.map +1 -0
  99. package/lib/paths.js +354 -0
  100. package/lib/paths.js.map +1 -0
  101. package/lib/plugin-update.js +1003 -0
  102. package/lib/plugin-update.js.map +1 -0
  103. package/lib/runtime-install.js +964 -0
  104. package/lib/runtime-install.js.map +1 -0
  105. package/lib/runtime-manager.js +133 -0
  106. package/lib/runtime-manager.js.map +1 -0
  107. package/lib/runtime.js +1678 -0
  108. package/lib/runtime.js.map +1 -0
  109. package/lib/skill.js +25 -0
  110. package/lib/skill.js.map +1 -0
  111. package/lib/tools.js +549 -0
  112. package/lib/tools.js.map +1 -0
  113. package/lib/types/artifact-access.d.ts +61 -0
  114. package/lib/types/artifact-access.d.ts.map +1 -0
  115. package/lib/types/artifacts.d.ts +42 -0
  116. package/lib/types/artifacts.d.ts.map +1 -0
  117. package/lib/types/client/display-config.d.ts +24 -0
  118. package/lib/types/client/display-config.d.ts.map +1 -0
  119. package/lib/types/client/index.d.ts +350 -0
  120. package/lib/types/client/index.d.ts.map +1 -0
  121. package/lib/types/client/model-variants-hider.d.ts +40 -0
  122. package/lib/types/client/model-variants-hider.d.ts.map +1 -0
  123. package/lib/types/client/paste-images.d.ts +126 -0
  124. package/lib/types/client/paste-images.d.ts.map +1 -0
  125. package/lib/types/config.d.ts +123 -0
  126. package/lib/types/config.d.ts.map +1 -0
  127. package/lib/types/defaults.d.ts +6 -0
  128. package/lib/types/defaults.d.ts.map +1 -0
  129. package/lib/types/errors.d.ts +35 -0
  130. package/lib/types/errors.d.ts.map +1 -0
  131. package/lib/types/evidence-cache.d.ts +91 -0
  132. package/lib/types/evidence-cache.d.ts.map +1 -0
  133. package/lib/types/exposure.d.ts +50 -0
  134. package/lib/types/exposure.d.ts.map +1 -0
  135. package/lib/types/image-input-variants.d.ts +144 -0
  136. package/lib/types/image-input-variants.d.ts.map +1 -0
  137. package/lib/types/index.d.ts +19 -0
  138. package/lib/types/index.d.ts.map +1 -0
  139. package/lib/types/paste-images.d.ts +73 -0
  140. package/lib/types/paste-images.d.ts.map +1 -0
  141. package/lib/types/paths.d.ts +117 -0
  142. package/lib/types/paths.d.ts.map +1 -0
  143. package/lib/types/plugin-update.d.ts +119 -0
  144. package/lib/types/plugin-update.d.ts.map +1 -0
  145. package/lib/types/runtime-install.d.ts +92 -0
  146. package/lib/types/runtime-install.d.ts.map +1 -0
  147. package/lib/types/runtime-manager.d.ts +60 -0
  148. package/lib/types/runtime-manager.d.ts.map +1 -0
  149. package/lib/types/runtime.d.ts +412 -0
  150. package/lib/types/runtime.d.ts.map +1 -0
  151. package/lib/types/skill.d.ts +14 -0
  152. package/lib/types/skill.d.ts.map +1 -0
  153. package/lib/types/tools.d.ts +35 -0
  154. package/lib/types/tools.d.ts.map +1 -0
  155. package/lib/types/upstream.d.ts +233 -0
  156. package/lib/types/upstream.d.ts.map +1 -0
  157. package/lib/types/version.d.ts +15 -0
  158. package/lib/types/version.d.ts.map +1 -0
  159. package/lib/types/web-request.d.ts +11 -0
  160. package/lib/types/web-request.d.ts.map +1 -0
  161. package/lib/types/web.d.ts +118 -0
  162. package/lib/types/web.d.ts.map +1 -0
  163. package/lib/upstream.js +860 -0
  164. package/lib/upstream.js.map +1 -0
  165. package/lib/version.js +18 -0
  166. package/lib/version.js.map +1 -0
  167. package/lib/web-request.js +29 -0
  168. package/lib/web-request.js.map +1 -0
  169. package/lib/web.js +462 -0
  170. package/lib/web.js.map +1 -0
  171. package/package.json +149 -0
  172. package/patches/vision-tools-dsh.patch +970 -0
  173. package/runtime/requirements.lock +3 -0
  174. package/src/artifact-access.ts +386 -0
  175. package/src/artifacts.ts +85 -0
  176. package/src/client/display-config.ts +62 -0
  177. package/src/client/index.tsx +1580 -0
  178. package/src/client/model-variants-hider.ts +159 -0
  179. package/src/client/paste-images.tsx +747 -0
  180. package/src/config.ts +290 -0
  181. package/src/defaults.ts +5 -0
  182. package/src/errors.ts +62 -0
  183. package/src/evidence-cache.ts +418 -0
  184. package/src/exposure.ts +265 -0
  185. package/src/image-input-variants.ts +996 -0
  186. package/src/index.ts +141 -0
  187. package/src/paste-images.ts +286 -0
  188. package/src/paths.ts +383 -0
  189. package/src/plugin-update.ts +1143 -0
  190. package/src/runtime-install.ts +1161 -0
  191. package/src/runtime-manager.ts +174 -0
  192. package/src/runtime.ts +2189 -0
  193. package/src/skill.ts +35 -0
  194. package/src/tools.ts +691 -0
  195. package/src/upstream.ts +1078 -0
  196. package/src/version.ts +37 -0
  197. package/src/web-request.ts +27 -0
  198. package/src/web.ts +594 -0
  199. package/vendor/agent-vision-toolkit/CHANGELOG.md +30 -0
  200. package/vendor/agent-vision-toolkit/LICENSE +21 -0
  201. package/vendor/agent-vision-toolkit/README.md +405 -0
  202. package/vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json +94 -0
  203. package/vendor/agent-vision-toolkit/bin/crop +90 -0
  204. package/vendor/agent-vision-toolkit/bin/detect +13 -0
  205. package/vendor/agent-vision-toolkit/bin/glance +93 -0
  206. package/vendor/agent-vision-toolkit/bin/ground +13 -0
  207. package/vendor/agent-vision-toolkit/bin/trace +129 -0
  208. package/vendor/agent-vision-toolkit/detect.py +60 -0
  209. package/vendor/agent-vision-toolkit/ground.py +267 -0
  210. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/dominant_colors.py +224 -0
  211. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/extract_fg.py +278 -0
  212. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/html_shot.py +420 -0
  213. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/long_screenshot_ocr.py +1245 -0
  214. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/pixel_diff.py +88 -0
  215. package/vendor/agent-vision-toolkit/tests/test_vision_client.py +450 -0
  216. package/vendor/agent-vision-toolkit/vision_client.py +310 -0
package/src/config.ts ADDED
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Plugin configuration: provider endpoint and credential reference, output
3
+ * language, limits, and the external upstream runtime location. Secrets never
4
+ * live here — `provider.credential` is a DSH Credential reference resolved per
5
+ * operation through `ctx.credentials`.
6
+ * @module dsh-vision-toolkit/config
7
+ */
8
+
9
+ import z from '@deepseek-ai/schemastery'
10
+ import type Schema from '@deepseek-ai/schemastery'
11
+ import { credentialRef, type CredentialRef } from '@deepseek-ai/dsh-credentials'
12
+ import { settingsNamespace } from '@deepseek-ai/dsh-settings'
13
+ import { VisionToolkitError } from './errors.ts'
14
+ import {
15
+ BUILT_IN_FREE_VISION_BASE_URL,
16
+ BUILT_IN_FREE_VISION_CREDENTIAL,
17
+ BUILT_IN_FREE_VISION_MODEL,
18
+ } from './defaults.ts'
19
+
20
+ export {
21
+ BUILT_IN_FREE_VISION_BASE_URL,
22
+ BUILT_IN_FREE_VISION_CREDENTIAL,
23
+ BUILT_IN_FREE_VISION_KEY,
24
+ BUILT_IN_FREE_VISION_MODEL,
25
+ } from './defaults.ts'
26
+
27
+ /** Settings document namespace owned by this plugin. */
28
+ export const VISION_TOOLKIT_SETTINGS_NAMESPACE = settingsNamespace('vision-toolkit')
29
+
30
+ /** Browser-compatible default shared with the vendored Python client. */
31
+ export const DEFAULT_VISION_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
32
+
33
+ const BUILT_IN_FREE_VISION_MODEL_ALIASES = new Set([
34
+ BUILT_IN_FREE_VISION_MODEL,
35
+ 'gemini-3.7-flash',
36
+ 'qwen/qwen3.6-27b',
37
+ 'qwen3.6-27b',
38
+ 'gemma-4-26b-a4b-it',
39
+ 'gemma-4-26b',
40
+ '@cf/google/gemma-4-26b-a4b-it',
41
+ '@cf/moondream/moondream3.1-9B-A2B',
42
+ 'moondream',
43
+ 'moondream-3.1',
44
+ 'moondream3.1-9B-A2B',
45
+ ])
46
+
47
+ /** Full user-facing configuration; every field defaults at the schema boundary. */
48
+ export interface VisionToolkitConfig {
49
+ provider?: {
50
+ /** Provider API base URL. */
51
+ baseUrl?: string
52
+ /** DSH Credential reference holding the API key (an environment-style name). */
53
+ credential?: string
54
+ /** Multimodal model name. */
55
+ model?: string
56
+ /** Vision request protocol: OpenAI Chat Completions or Anthropic Messages. */
57
+ protocol?: 'openai' | 'anthropic'
58
+ /** Anthropic thinking field behavior; `omit` leaves model defaults untouched. */
59
+ anthropicThinking?: 'omit' | 'disabled' | 'adaptive'
60
+ /** Outbound User-Agent for provider requests and connection tests. */
61
+ userAgent?: string
62
+ }
63
+ /** Vision output language (`zh` or `en`). */
64
+ language?: 'zh' | 'en'
65
+ /** Single remote/upstream call budget in milliseconds. */
66
+ timeoutMs?: number
67
+ /** Maximum input image size in bytes; larger images are auto-compressed (lossless first). */
68
+ maxImageBytes?: number
69
+ /** Maximum decoded pixel count per input image; larger images are auto-downscaled to fit. */
70
+ maxImagePixels?: number
71
+ /** In-flight tool execution cap per session. */
72
+ concurrency?: number
73
+ runtime?: {
74
+ /** `managed` uses the packaged snapshot and isolated venv; `external` uses a clean pinned checkout. */
75
+ mode?: 'managed' | 'external'
76
+ /** Required path to the clean pinned checkout when `mode` is `external`. */
77
+ agentVisionToolkitPath?: string
78
+ /** Optional Python 3.11+ bootstrap/interpreter override. */
79
+ python?: string
80
+ }
81
+ /** Extra directories (besides the workspace) inputs may come from. */
82
+ allowedDirs?: string[]
83
+ /**
84
+ * Image-input variants: sibling model-selector entries for every model the
85
+ * host positively declares text-only. A variant declares image input, so
86
+ * pasted images keep the native attachment flow (composer thumbnail and
87
+ * durable session image), and the plugin rewrites image blocks into Vision
88
+ * Toolkit descriptions only on the wire to the model.
89
+ */
90
+ imageInputVariants?: {
91
+ /** Whether variant routes are registered at all (default true). */
92
+ enabled?: boolean
93
+ /** Restrict wrapped upstream routes by provider id; empty wraps every eligible route. */
94
+ providers?: string[]
95
+ /**
96
+ * Whether the browser paste integration automatically switches the Session
97
+ * to the image-input variant of a text-only model before the paste, so
98
+ * pasted images keep the native attachment flow with no manual model
99
+ * change. The variant still exposes a workspace path to the model; off
100
+ * keeps the path-only takeover instead (default true).
101
+ */
102
+ autoSwitch?: boolean
103
+ /**
104
+ * Transparent routing: variant routes keep the upstream provider and model
105
+ * display names, and the browser integration hides the upstream text-only
106
+ * entries that have a variant twin, so the model selector shows one entry
107
+ * per model and sessions stay on the image-capable variant without users
108
+ * seeing or switching a `(Vision Toolkit)` route. On by default; disable
109
+ * to restore the explicit sibling entries.
110
+ */
111
+ hidden?: boolean
112
+ }
113
+ }
114
+
115
+ /** Configuration schema with the documented P0 defaults. */
116
+ export const Config: Schema<VisionToolkitConfig> = z.object({
117
+ provider: z.object({
118
+ baseUrl: z.string().default(BUILT_IN_FREE_VISION_BASE_URL),
119
+ credential: z.string().default(BUILT_IN_FREE_VISION_CREDENTIAL),
120
+ model: z.string().default(BUILT_IN_FREE_VISION_MODEL),
121
+ protocol: z.union(['openai', 'anthropic'] as const).default('openai'),
122
+ anthropicThinking: z.union(['omit', 'disabled', 'adaptive'] as const).default('omit'),
123
+ userAgent: z.string().default(DEFAULT_VISION_USER_AGENT),
124
+ }),
125
+ language: z.union(['zh', 'en'] as const).default('zh'),
126
+ timeoutMs: z.number().default(30000),
127
+ maxImageBytes: z.number().default(4194304),
128
+ maxImagePixels: z.number().default(20000000),
129
+ concurrency: z.number().default(4),
130
+ runtime: z.object({
131
+ mode: z.union(['managed', 'external'] as const).default('managed'),
132
+ agentVisionToolkitPath: z.string(),
133
+ python: z.string(),
134
+ }),
135
+ allowedDirs: z.array(z.string()).default([]),
136
+ imageInputVariants: z.object({
137
+ enabled: z.boolean().default(true),
138
+ providers: z.array(z.string()).default([]),
139
+ autoSwitch: z.boolean().default(true),
140
+ hidden: z.boolean().default(true),
141
+ }),
142
+ })
143
+
144
+ /** Configuration after static validation, with every default materialized. */
145
+ export interface ResolvedVisionToolkitConfig {
146
+ provider: {
147
+ baseUrl: string
148
+ credential: CredentialRef
149
+ model: string
150
+ protocol: 'openai' | 'anthropic'
151
+ anthropicThinking: 'omit' | 'disabled' | 'adaptive'
152
+ userAgent: string
153
+ }
154
+ language: 'zh' | 'en'
155
+ timeoutMs: number
156
+ maxImageBytes: number
157
+ maxImagePixels: number
158
+ concurrency: number
159
+ runtime: {
160
+ mode: 'managed' | 'external'
161
+ agentVisionToolkitPath?: string
162
+ python?: string
163
+ }
164
+ allowedDirs: string[]
165
+ imageInputVariants: {
166
+ enabled: boolean
167
+ providers: string[]
168
+ autoSwitch: boolean
169
+ hidden: boolean
170
+ }
171
+ }
172
+
173
+ const MAX_TIMEOUT_MS = 600000
174
+ const MAX_IMAGE_BYTES = 268435456
175
+ const MAX_IMAGE_PIXELS = 268435456
176
+ const MAX_CONCURRENCY = 16
177
+
178
+ /**
179
+ * Validate and normalize a config object (partial inputs receive the same
180
+ * defaults the schemastery schema applies). Configuration mistakes fail loud
181
+ * at plugin load (the earliest resolvable point); runtime availability is a
182
+ * separate, later concern.
183
+ * @param config - parsed config with defaults applied.
184
+ * @returns the fully defaulted, validated configuration.
185
+ */
186
+ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionToolkitConfig {
187
+ const provider = config.provider ?? {}
188
+ const runtime = config.runtime ?? {}
189
+ const baseUrl = (provider.baseUrl ?? BUILT_IN_FREE_VISION_BASE_URL).trim().replace(/\/+$/, '')
190
+ if (!/^https?:\/\//i.test(baseUrl) || baseUrl.length <= 'https://'.length) {
191
+ throw new VisionToolkitError('config', 'provider.baseUrl must be an http(s) URL')
192
+ }
193
+ let credential: CredentialRef
194
+ try {
195
+ credential = credentialRef((provider.credential ?? BUILT_IN_FREE_VISION_CREDENTIAL).trim())
196
+ } catch (error) {
197
+ throw new VisionToolkitError(
198
+ 'config',
199
+ `provider.credential "${provider.credential ?? BUILT_IN_FREE_VISION_CREDENTIAL}" is not a valid credential reference`,
200
+ { cause: error },
201
+ )
202
+ }
203
+ const model = (provider.model ?? BUILT_IN_FREE_VISION_MODEL).trim()
204
+ if (model.length === 0) {
205
+ throw new VisionToolkitError('config', 'provider.model must not be empty')
206
+ }
207
+ const protocol = provider.protocol ?? 'openai'
208
+ if (protocol !== 'openai' && protocol !== 'anthropic') {
209
+ throw new VisionToolkitError('config', 'provider.protocol must be "openai" or "anthropic"')
210
+ }
211
+ const anthropicThinking = provider.anthropicThinking ?? 'omit'
212
+ if (anthropicThinking !== 'omit' && anthropicThinking !== 'disabled' && anthropicThinking !== 'adaptive') {
213
+ throw new VisionToolkitError('config', 'provider.anthropicThinking must be "omit", "disabled", or "adaptive"')
214
+ }
215
+ const userAgent = (provider.userAgent ?? DEFAULT_VISION_USER_AGENT).trim()
216
+ if (userAgent.length === 0) {
217
+ throw new VisionToolkitError('config', 'provider.userAgent must not be empty')
218
+ }
219
+ const language = config.language ?? 'zh'
220
+ if (language !== 'zh' && language !== 'en') {
221
+ throw new VisionToolkitError('config', 'language must be "zh" or "en"')
222
+ }
223
+ const timeoutMs = config.timeoutMs ?? 30000
224
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > MAX_TIMEOUT_MS) {
225
+ throw new VisionToolkitError('config', `timeoutMs must be an integer between 1000 and ${MAX_TIMEOUT_MS}`)
226
+ }
227
+ const maxImageBytes = config.maxImageBytes ?? 4194304
228
+ if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
229
+ throw new VisionToolkitError('config', `maxImageBytes must be an integer between 1024 and ${MAX_IMAGE_BYTES}`)
230
+ }
231
+ const maxImagePixels = config.maxImagePixels ?? 20000000
232
+ if (!Number.isInteger(maxImagePixels) || maxImagePixels < 1 || maxImagePixels > MAX_IMAGE_PIXELS) {
233
+ throw new VisionToolkitError('config', `maxImagePixels must be an integer between 1 and ${MAX_IMAGE_PIXELS}`)
234
+ }
235
+ const concurrency = config.concurrency ?? 4
236
+ if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > MAX_CONCURRENCY) {
237
+ throw new VisionToolkitError('config', `concurrency must be an integer between 1 and ${MAX_CONCURRENCY}`)
238
+ }
239
+ const mode = runtime.mode ?? 'managed'
240
+ if (mode !== 'managed' && mode !== 'external') {
241
+ throw new VisionToolkitError('config', 'runtime.mode must be "managed" or "external"')
242
+ }
243
+ const toolkitPath = runtime.agentVisionToolkitPath?.trim()
244
+ if (toolkitPath !== undefined && toolkitPath.length === 0) {
245
+ throw new VisionToolkitError('config', 'runtime.agentVisionToolkitPath must not be empty when provided')
246
+ }
247
+ if (mode === 'external' && toolkitPath === undefined) {
248
+ throw new VisionToolkitError('config', 'runtime.agentVisionToolkitPath is required when runtime.mode is external')
249
+ }
250
+ if (mode === 'managed' && toolkitPath !== undefined) {
251
+ throw new VisionToolkitError('config', 'runtime.agentVisionToolkitPath is only valid when runtime.mode is external')
252
+ }
253
+ const python = runtime.python?.trim()
254
+ if (python !== undefined && python.length === 0) {
255
+ throw new VisionToolkitError('config', 'runtime.python must not be empty')
256
+ }
257
+ const allowedDirs = (config.allowedDirs ?? []).map(dir => dir.trim()).filter(dir => dir.length > 0)
258
+ const imageInputVariants = config.imageInputVariants ?? {}
259
+ const variantProviders = (imageInputVariants.providers ?? [])
260
+ .map(provider => provider.trim())
261
+ .filter(provider => provider.length > 0)
262
+ return {
263
+ provider: { baseUrl, credential, model, protocol, anthropicThinking, userAgent },
264
+ language,
265
+ timeoutMs,
266
+ maxImageBytes,
267
+ maxImagePixels,
268
+ concurrency,
269
+ runtime: {
270
+ mode,
271
+ ...(toolkitPath !== undefined ? { agentVisionToolkitPath: toolkitPath } : {}),
272
+ ...(python !== undefined ? { python } : {}),
273
+ },
274
+ allowedDirs,
275
+ imageInputVariants: {
276
+ enabled: imageInputVariants.enabled ?? true,
277
+ providers: variantProviders,
278
+ autoSwitch: imageInputVariants.autoSwitch ?? true,
279
+ hidden: imageInputVariants.hidden ?? true,
280
+ },
281
+ }
282
+ }
283
+
284
+ /** Whether a resolved provider should use the bundled public key instead of DSH credentials. */
285
+ export function isBuiltInFreeVisionProvider(provider: ResolvedVisionToolkitConfig['provider']): boolean {
286
+ return String(provider.credential) === BUILT_IN_FREE_VISION_CREDENTIAL
287
+ && provider.baseUrl === BUILT_IN_FREE_VISION_BASE_URL
288
+ && BUILT_IN_FREE_VISION_MODEL_ALIASES.has(provider.model)
289
+ && provider.protocol === 'openai'
290
+ }
@@ -0,0 +1,5 @@
1
+ /** Public free vision service defaults shared by server and browser settings. */
2
+ export const BUILT_IN_FREE_VISION_BASE_URL = 'https://vision.anionex.me/v1'
3
+ export const BUILT_IN_FREE_VISION_CREDENTIAL = 'ANIONEX_FREE_VISION'
4
+ export const BUILT_IN_FREE_VISION_KEY = 'https://agent-vision.anionex.me'
5
+ export const BUILT_IN_FREE_VISION_MODEL = 'gemini-3.7-flash'
package/src/errors.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Stable error vocabulary shared by the runtime, upstream adapter, and tools.
3
+ * Every failure reaching the model carries one of these codes and a message
4
+ * that never contains credentials or raw upstream stack traces.
5
+ * @module dsh-vision-toolkit/errors
6
+ */
7
+
8
+ /** Discriminant tag for every Vision Toolkit failure. */
9
+ export const VISION_TOOLKIT_ERROR_CODES = [
10
+ 'config',
11
+ 'input',
12
+ 'capacity',
13
+ 'service',
14
+ 'runtime',
15
+ 'output',
16
+ 'timeout',
17
+ 'cancelled',
18
+ 'path',
19
+ ] as const
20
+
21
+ /** Stable machine-readable error category. */
22
+ export type VisionToolkitErrorCode = typeof VISION_TOOLKIT_ERROR_CODES[number]
23
+
24
+ /** Error with a stable category; safe to surface to the model. */
25
+ export class VisionToolkitError extends Error {
26
+ readonly code: VisionToolkitErrorCode
27
+
28
+ constructor(code: VisionToolkitErrorCode, message: string, options?: { cause?: unknown }) {
29
+ super(message, options)
30
+ this.name = 'VisionToolkitError'
31
+ this.code = code
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Replace every known secret occurrence in untrusted text. Used before
37
+ * upstream stderr, exit messages, or trace reports enter logs or results.
38
+ * @param text - text that may embed a secret.
39
+ * @param secrets - values that must never be surfaced.
40
+ * @returns text with each secret replaced by a fixed marker.
41
+ */
42
+ export function redactText(text: string, secrets: readonly string[]): string {
43
+ let result = text
44
+ for (const secret of secrets) {
45
+ if (secret.length === 0) continue
46
+ result = result.split(secret).join('<redacted>')
47
+ }
48
+ return result
49
+ }
50
+
51
+ /**
52
+ * Build a model-safe upstream failure line: the tool prefix plus the
53
+ * redacted stderr tail, never a JavaScript stack.
54
+ * @param tool - upstream CLI name.
55
+ * @param stderr - captured upstream stderr.
56
+ * @param secrets - values to redact.
57
+ * @returns one-line safe message.
58
+ */
59
+ export function upstreamFailureMessage(tool: string, stderr: string, secrets: readonly string[]): string {
60
+ const tail = redactText(stderr, secrets).trim().split(/\r?\n/).filter(Boolean).slice(-2).join(' ')
61
+ return tail.length === 0 ? `${tool}: upstream command failed` : `${tool}: ${tail}`
62
+ }