dsh-draw 0.1.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +201 -0
  3. package/README.es.md +194 -0
  4. package/README.hi.md +194 -0
  5. package/README.md +194 -0
  6. package/README.pt.md +194 -0
  7. package/README.zh.md +194 -0
  8. package/SECURITY.md +39 -0
  9. package/THIRD_PARTY_NOTICES.md +21 -0
  10. package/cordis.patch.yml +48 -0
  11. package/lib/client.js +4787 -0
  12. package/lib/client.js.map +1 -0
  13. package/lib/index.js +1429 -0
  14. package/lib/typert.host.js +26 -0
  15. package/lib/types/client/DrawResultCard.d.ts +58 -0
  16. package/lib/types/client/DrawResultCard.d.ts.map +1 -0
  17. package/lib/types/client/DrawResultCard.js +48 -0
  18. package/lib/types/client/DrawSettingsTab.d.ts +31 -0
  19. package/lib/types/client/DrawSettingsTab.d.ts.map +1 -0
  20. package/lib/types/client/DrawSettingsTab.js +129 -0
  21. package/lib/types/client/index.d.ts +35 -0
  22. package/lib/types/client/index.d.ts.map +1 -0
  23. package/lib/types/client/index.js +98 -0
  24. package/lib/types/client/locales.d.ts +14 -0
  25. package/lib/types/client/locales.d.ts.map +1 -0
  26. package/lib/types/client/locales.js +57 -0
  27. package/lib/types/client/present.d.ts +80 -0
  28. package/lib/types/client/present.d.ts.map +1 -0
  29. package/lib/types/client/present.js +86 -0
  30. package/lib/types/client/remote.d.ts +268 -0
  31. package/lib/types/client/remote.d.ts.map +1 -0
  32. package/lib/types/client/remote.js +15 -0
  33. package/lib/types/client/styles.d.ts +11 -0
  34. package/lib/types/client/styles.d.ts.map +1 -0
  35. package/lib/types/client/styles.js +43 -0
  36. package/lib/types/config.d.ts +160 -0
  37. package/lib/types/config.d.ts.map +1 -0
  38. package/lib/types/config.js +230 -0
  39. package/lib/types/drawer.d.ts +114 -0
  40. package/lib/types/drawer.d.ts.map +1 -0
  41. package/lib/types/drawer.js +138 -0
  42. package/lib/types/engine.d.ts +58 -0
  43. package/lib/types/engine.d.ts.map +1 -0
  44. package/lib/types/engine.js +135 -0
  45. package/lib/types/http.d.ts +89 -0
  46. package/lib/types/http.d.ts.map +1 -0
  47. package/lib/types/http.js +127 -0
  48. package/lib/types/index.d.ts +43 -0
  49. package/lib/types/index.d.ts.map +1 -0
  50. package/lib/types/index.js +78 -0
  51. package/lib/types/quota.d.ts +69 -0
  52. package/lib/types/quota.d.ts.map +1 -0
  53. package/lib/types/quota.js +56 -0
  54. package/lib/types/router.d.ts +141 -0
  55. package/lib/types/router.d.ts.map +1 -0
  56. package/lib/types/router.js +207 -0
  57. package/lib/types/sanitize.d.ts +40 -0
  58. package/lib/types/sanitize.d.ts.map +1 -0
  59. package/lib/types/sanitize.js +103 -0
  60. package/lib/types/service.d.ts +59 -0
  61. package/lib/types/service.d.ts.map +1 -0
  62. package/lib/types/service.js +131 -0
  63. package/lib/types/session-events.d.ts +66 -0
  64. package/lib/types/session-events.d.ts.map +1 -0
  65. package/lib/types/session-events.js +32 -0
  66. package/lib/types/tool.d.ts +30 -0
  67. package/lib/types/tool.d.ts.map +1 -0
  68. package/lib/types/tool.js +131 -0
  69. package/lib/types/translate.d.ts +64 -0
  70. package/lib/types/translate.d.ts.map +1 -0
  71. package/lib/types/translate.js +56 -0
  72. package/lib/types/typert.host.d.ts +250 -0
  73. package/lib/types/typert.host.d.ts.map +1 -0
  74. package/lib/types/typert.host.js +23 -0
  75. package/lib/types/version.d.ts +10 -0
  76. package/lib/types/version.d.ts.map +1 -0
  77. package/lib/types/version.js +9 -0
  78. package/lib/types/wire.d.ts +699 -0
  79. package/lib/types/wire.d.ts.map +1 -0
  80. package/lib/types/wire.js +273 -0
  81. package/lib/wire-Cc4JZ3jR.js +4370 -0
  82. package/package.json +179 -0
  83. package/src/client/DrawResultCard.tsx +100 -0
  84. package/src/client/DrawSettingsTab.tsx +159 -0
  85. package/src/client/index.ts +123 -0
  86. package/src/client/locales.ts +84 -0
  87. package/src/client/present.ts +137 -0
  88. package/src/client/remote.ts +44 -0
  89. package/src/client/styles.ts +44 -0
  90. package/src/config.ts +358 -0
  91. package/src/drawer.ts +234 -0
  92. package/src/engine.ts +182 -0
  93. package/src/http.ts +161 -0
  94. package/src/index.ts +93 -0
  95. package/src/quota.ts +98 -0
  96. package/src/router.ts +309 -0
  97. package/src/sanitize.ts +113 -0
  98. package/src/service.ts +169 -0
  99. package/src/session-events.ts +70 -0
  100. package/src/tool.ts +145 -0
  101. package/src/translate.ts +101 -0
  102. package/src/typert.host.ts +25 -0
  103. package/src/version.ts +10 -0
  104. package/src/wire.ts +417 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The dsh-draw session event: one durable record per completed generation
3
+ * (tool call or panel regenerate), appended to the owning session log. It is
4
+ * log-only (`ignorable`) and exists so quota accounting and the durable audit
5
+ * trail are reconstructable from the log alone. The model-visible input and
6
+ * output of the call ride the loop-owned `tool/call` and `tool/result`
7
+ * events; this event carries the accounting facts those events do not.
8
+ *
9
+ * @module dsh-draw/session-events
10
+ */
11
+
12
+ import type { Session } from '@deepseek-ai/dsh-session'
13
+
14
+ declare module '@deepseek-ai/dsh-session/types' {
15
+ interface SessionEventMap {
16
+ /**
17
+ * One completed image generation: engine, standardized request, produced
18
+ * bytes, and the durable attachment ids. Log-only UI/accounting state.
19
+ */
20
+ 'draw/generated': DrawGeneratedEvent
21
+ }
22
+ }
23
+
24
+ /** Payload of the `draw/generated` session event. */
25
+ export interface DrawGeneratedEvent {
26
+ /** Engine id that produced the images. */
27
+ engine: string
28
+ /** Engine model name. */
29
+ model: string
30
+ /** Who requested the generation: the tool body or a panel regenerate. */
31
+ source: 'tool' | 'regenerate'
32
+ /** The standardized prompt, verbatim from the model request. */
33
+ prompt: string
34
+ /** Standard size vocabulary value of the request. */
35
+ size: 'square' | 'landscape' | 'portrait' | 'auto'
36
+ /** Standard quality value of the request. */
37
+ quality: 'low' | 'medium' | 'high' | 'auto'
38
+ /** Number of images produced. */
39
+ count: number
40
+ /** Sum of the produced image byte lengths. */
41
+ bytes: number
42
+ /** Durable attachment ids in result order. */
43
+ attachmentIds: readonly string[]
44
+ /** Engine round-trip latency in milliseconds. */
45
+ elapsedMs: number
46
+ }
47
+
48
+ /**
49
+ * Append one `draw/generated` event to a session.
50
+ *
51
+ * @param session - owning session.
52
+ * @param event - accounting payload.
53
+ * @returns the logged event.
54
+ */
55
+ export function appendDrawGenerated(session: Session, event: DrawGeneratedEvent) {
56
+ // Two-argument append: the pinned 0.1.0-rc.6 peers have no append-envelope
57
+ // option; the two-argument form typechecks against rc.6 and newer builds.
58
+ return session.append('draw/generated', event)
59
+ }
60
+
61
+ /**
62
+ * Collect the `draw/generated` events of a session in log order. Declaration
63
+ * merging narrows `event.type === 'draw/generated'` to the payload type.
64
+ *
65
+ * @param session - session whose log is scanned.
66
+ * @returns the accounting events.
67
+ */
68
+ export function drawGeneratedEvents(session: Session): readonly import('@deepseek-ai/dsh-session').SessionEvent<'draw/generated'>[] {
69
+ return session.events.filter(event => event.type === 'draw/generated')
70
+ }
package/src/tool.ts ADDED
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The `image_generate` tool: the model-facing unified image generation entry.
3
+ * Standard parameters (prompt/size/count/quality/style/engine) flow into the
4
+ * shared drawer path; the canonical value carries the durable attachment
5
+ * references plus quota and routing facts, and the render output embeds the
6
+ * images as attachment content blocks so both text-only surfaces and the web
7
+ * conversation show them.
8
+ *
9
+ * @module dsh-draw/tool
10
+ */
11
+
12
+ import { AttachmentId, type ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
13
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm'
14
+ import type { JsonValue } from '@deepseek-ai/dsh-session'
15
+ import { defineTool } from '@deepseek-ai/dsh-tools'
16
+ import type { ResolvedConfig } from './config.ts'
17
+ import type { Drawer, DrawSuccess } from './drawer.ts'
18
+
19
+ /** Tool argument surface (validated by the schema; the drawer re-normalizes). */
20
+ export interface ImageGenerateArgs {
21
+ prompt: string
22
+ size?: 'square' | 'landscape' | 'portrait' | 'auto'
23
+ count?: number
24
+ quality?: 'low' | 'medium' | 'high' | 'auto'
25
+ style?: 'natural' | 'vivid'
26
+ engine?: string
27
+ }
28
+
29
+ /**
30
+ * Build the `image_generate` tool over one drawer.
31
+ *
32
+ * @param drawer - the shared generation path.
33
+ * @param config - resolved config (prompt cap for the description, timeout budget).
34
+ * @returns the tool definition.
35
+ */
36
+ export function imageGenerateTool(drawer: Drawer, config: ResolvedConfig) {
37
+ return defineTool({
38
+ name: 'image_generate',
39
+ description: `Generate 1..${config.maxImagesPerCall} static images from a text prompt through the configured image engines (OpenAI Images, Zhipu CogView, or any OpenAI-compatible endpoint). Standard parameters are translated per engine; a failing engine falls back down the configured chain. Results are saved as durable attachments and count against the per-session quota. Prompt cap: ${config.maxPromptLength} characters.`,
40
+ parameters: {
41
+ prompt: { type: 'string' as const, description: 'Image prompt.', required: true as const },
42
+ size: { type: 'string' as const, enum: ['square', 'landscape', 'portrait', 'auto'] as const, description: 'Composition (default square).' },
43
+ count: { type: 'integer' as const, description: 'Number of images (default 1; capped by configuration).' },
44
+ quality: { type: 'string' as const, enum: ['low', 'medium', 'high', 'auto'] as const, description: 'Quality tier (default auto; dropped for engines without it).' },
45
+ style: { type: 'string' as const, enum: ['natural', 'vivid'] as const, description: 'Style preset (dropped for engines without it).' },
46
+ engine: { type: 'string' as const, description: 'Engine id override (must name a configured engine; omitted = chain order).' },
47
+ },
48
+ output: {
49
+ schema: {
50
+ type: 'object' as const,
51
+ properties: {
52
+ ok: { type: 'boolean' as const, const: true },
53
+ engine: { type: 'string' as const },
54
+ model: { type: 'string' as const },
55
+ size: { type: 'string' as const },
56
+ images: {
57
+ type: 'array' as const,
58
+ items: {
59
+ type: 'object' as const,
60
+ properties: {
61
+ attachmentId: { type: 'string' as const },
62
+ mediaType: { type: 'string' as const },
63
+ bytes: { type: 'integer' as const },
64
+ width: { type: 'integer' as const },
65
+ height: { type: 'integer' as const },
66
+ name: { type: 'string' as const },
67
+ },
68
+ additionalProperties: false,
69
+ },
70
+ },
71
+ quota: {
72
+ type: 'object' as const,
73
+ properties: {
74
+ generations: { type: 'integer' as const },
75
+ bytes: { type: 'integer' as const },
76
+ },
77
+ additionalProperties: false,
78
+ },
79
+ limits: {
80
+ type: 'object' as const,
81
+ properties: {
82
+ maxGenerations: { type: 'integer' as const },
83
+ maxBytes: { type: 'integer' as const },
84
+ },
85
+ additionalProperties: false,
86
+ },
87
+ fallbackUsed: { type: 'boolean' as const },
88
+ elapsedMs: { type: 'integer' as const },
89
+ attempts: {
90
+ type: 'array' as const,
91
+ items: {
92
+ type: 'object' as const,
93
+ properties: {
94
+ engine: { type: 'string' as const },
95
+ phase: { oneOf: [{ type: 'string' as const }, { type: 'null' as const }] as const },
96
+ code: { type: 'string' as const },
97
+ message: { oneOf: [{ type: 'string' as const }, { type: 'null' as const }] as const },
98
+ status: { oneOf: [{ type: 'integer' as const }, { type: 'null' as const }] as const },
99
+ },
100
+ additionalProperties: false,
101
+ },
102
+ },
103
+ },
104
+ additionalProperties: false,
105
+ },
106
+ render(_args, value): ContentBlock[] {
107
+ const result = value as unknown as DrawSuccess
108
+ const lines = [
109
+ `Generated ${result.images.length} image(s) via ${result.engine} (${result.model}, ${result.size})${result.fallbackUsed ? ' after fallback' : ''} in ${result.elapsedMs} ms.`,
110
+ `Quota: ${result.quota.generations}/${result.limits.maxGenerations} calls, ${result.quota.bytes}/${result.limits.maxBytes} bytes this session.`,
111
+ ]
112
+ for (const attempt of result.attempts) {
113
+ if (attempt.code === 'ok') continue
114
+ lines.push(`- ${attempt.engine}: ${attempt.code}${attempt.message === undefined ? '' : ` (${attempt.message})`}`)
115
+ }
116
+ const blocks: ContentBlock[] = [{ type: 'text', text: lines.join('\n') }]
117
+ for (const image of result.images) {
118
+ const attachment: ImageAttachmentRef = {
119
+ attachmentId: AttachmentId(image.attachmentId),
120
+ mediaType: image.mediaType as ImageAttachmentRef['mediaType'],
121
+ bytes: image.bytes,
122
+ width: image.width,
123
+ height: image.height,
124
+ ...(image.name !== undefined ? { name: image.name } : {}),
125
+ }
126
+ blocks.push({ type: 'image', attachment })
127
+ }
128
+ return blocks
129
+ },
130
+ // The result card reads the canonical value through this replayable
131
+ // presentation projection (the frozen client block exposes only `meta`).
132
+ presentationMeta(_args, value): JsonValue {
133
+ return value as unknown as JsonValue
134
+ },
135
+ },
136
+ timeoutMs: config.requestTimeoutMs,
137
+ async execute(args, exec) {
138
+ const outcome = await drawer.generate(args as unknown, { session: exec.agent?.session, source: 'tool', signal: exec.signal })
139
+ if (!outcome.ok) throw new Error(outcome.message)
140
+ // The schema-inferred canonical value owns mutable arrays; the drawer's
141
+ // frozen records are copied into owned mutable ones here.
142
+ return { ...outcome, images: [...outcome.images], attempts: [...outcome.attempts] }
143
+ },
144
+ })
145
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Standard-parameter translation: the tool-level vocabulary
3
+ * (`prompt`/size/count/quality/style) mapped onto one concrete engine's
4
+ * request. Pure functions with no I/O — unit tests pin every mapping.
5
+ *
6
+ * @module dsh-draw/translate
7
+ */
8
+
9
+ import type { ResolvedEngineConfig, StandardSize } from './config.ts'
10
+
11
+ /** Standard quality vocabulary; `auto` means "leave the engine default". */
12
+ export type StandardQuality = 'low' | 'medium' | 'high' | 'auto'
13
+
14
+ /** Standard style vocabulary (engines that lack styles drop it). */
15
+ export type StandardStyle = 'natural' | 'vivid'
16
+
17
+ /** Standard tool-level image request. */
18
+ export interface StandardImageRequest {
19
+ /** Image prompt, non-empty and within the configured length cap. */
20
+ prompt: string
21
+ /** Standard size vocabulary (default `square`). */
22
+ size?: StandardSize
23
+ /** Number of images (default 1; capped by the engine/config). */
24
+ count?: number
25
+ /** Standard quality vocabulary (default `auto`). */
26
+ quality?: StandardQuality
27
+ /** Standard style vocabulary; omitted = engine default. */
28
+ style?: StandardStyle
29
+ /** Engine id override; omitted = the router's chain order. */
30
+ engine?: string
31
+ }
32
+
33
+ /** Concrete per-engine request body fields. */
34
+ export interface TranslatedImageRequest {
35
+ /** Engine model name. */
36
+ model: string
37
+ /** Prompt, verbatim. */
38
+ prompt: string
39
+ /** Concrete engine size string. */
40
+ size: string
41
+ /** Number of images. */
42
+ n: number
43
+ /** Included only when the engine declares quality support and it is not `auto`. */
44
+ quality?: StandardQuality
45
+ /** Included only when the engine declares style support and one was requested. */
46
+ style?: StandardStyle
47
+ /** How the response delivers bytes. */
48
+ responseFormat: 'b64_json' | 'url'
49
+ }
50
+
51
+ const SIZES: readonly StandardSize[] = ['square', 'landscape', 'portrait', 'auto']
52
+ const QUALITIES: readonly StandardQuality[] = ['low', 'medium', 'high', 'auto']
53
+ const STYLES: readonly StandardStyle[] = ['natural', 'vivid']
54
+
55
+ /**
56
+ * Normalize unvalidated tool args to a {@link StandardImageRequest}; malformed
57
+ * values fall back to defaults. The prompt passes through verbatim — length
58
+ * validation is the drawer's job (an over-long prompt fails the call loudly
59
+ * instead of being silently truncated).
60
+ *
61
+ * @param args - unvalidated tool arguments.
62
+ * @param maxImagesPerCall - configured cap the requested count clamps to.
63
+ * @returns the normalized request.
64
+ */
65
+ export function normalizeRequest(args: unknown, maxImagesPerCall: number): StandardImageRequest {
66
+ const record = typeof args === 'object' && args !== null ? args as Record<string, unknown> : {}
67
+ const prompt = typeof record['prompt'] === 'string' ? record['prompt'] : ''
68
+ const rawSize = record['size']
69
+ const size = typeof rawSize === 'string' && (SIZES as readonly string[]).includes(rawSize) ? rawSize as StandardSize : 'square'
70
+ const rawCount = record['count']
71
+ const parsedCount = typeof rawCount === 'number' ? Math.trunc(rawCount) : 1
72
+ const count = Math.min(Math.max(Number.isFinite(parsedCount) ? parsedCount : 1, 1), maxImagesPerCall)
73
+ const rawQuality = record['quality']
74
+ const quality = typeof rawQuality === 'string' && (QUALITIES as readonly string[]).includes(rawQuality) ? rawQuality as StandardQuality : 'auto'
75
+ const rawStyle = record['style']
76
+ const style = typeof rawStyle === 'string' && (STYLES as readonly string[]).includes(rawStyle) ? rawStyle as StandardStyle : undefined
77
+ const rawEngine = record['engine']
78
+ const engine = typeof rawEngine === 'string' && rawEngine.length > 0 ? rawEngine : undefined
79
+ return { prompt, size, count, quality, ...(style !== undefined ? { style } : {}), ...(engine !== undefined ? { engine } : {}) }
80
+ }
81
+
82
+ /**
83
+ * Translate one standardized request into a concrete engine request.
84
+ *
85
+ * @param engine - resolved engine configuration.
86
+ * @param request - normalized standard request.
87
+ * @returns the engine request fields.
88
+ */
89
+ export function translateRequest(engine: ResolvedEngineConfig, request: StandardImageRequest): TranslatedImageRequest {
90
+ const size = engine.sizeMap[request.size ?? 'square']
91
+ const requestBody: TranslatedImageRequest = {
92
+ model: engine.model,
93
+ prompt: request.prompt,
94
+ size,
95
+ n: request.count ?? 1,
96
+ ...(engine.qualitySupported && request.quality !== 'auto' ? { quality: request.quality } : {}),
97
+ ...(engine.styleSupported && request.style !== undefined ? { style: request.style } : {}),
98
+ responseFormat: engine.responseFormat,
99
+ }
100
+ return requestBody
101
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The hand-written Typert HOST manifest for `dsh-draw`, exported as `./typert`
3
+ * so the harness's typert-loader registers the `draw` status/probe/credential/
4
+ * regenerate invocations automatically when this plugin mounts. Same shape as
5
+ * a generator output (validated by the loader): package face, empty model and
6
+ * schemas, and the canonical invocation list shared with the client Remote
7
+ * contribution (`src/wire.ts`).
8
+ *
9
+ * @module dsh-draw/typert
10
+ */
11
+
12
+ import { DRAW_INVOCATIONS } from './wire.ts'
13
+
14
+ /** Host Typert manifest (validated by `@deepseek-ai/dsh-typert-loader`). */
15
+ export const TYPERT = Object.freeze({
16
+ package: 'dsh-draw',
17
+ face: 'host',
18
+ schemas: Object.freeze([]),
19
+ invocations: DRAW_INVOCATIONS,
20
+ model: Object.freeze({
21
+ services: Object.freeze([]),
22
+ events: Object.freeze([]),
23
+ objects: Object.freeze([]),
24
+ }),
25
+ })
package/src/version.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The plugin version, hardcoded beside `package.json` so the release script
3
+ * bumps both and `tests/version.spec.ts` trips when they drift. Served in the
4
+ * `draw/status` snapshot so the settings panel shows which plugin build runs.
5
+ *
6
+ * @module dsh-draw/version
7
+ */
8
+
9
+ /** Plugin version; must equal the `version` field in `package.json`. */
10
+ export const PLUGIN_VERSION = '0.1.0'