howone 0.1.22 → 0.1.25

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 (26) hide show
  1. package/package.json +1 -1
  2. package/templates/nextjs/lib/sdk.ts +3 -0
  3. package/templates/vite/.howone/skills/howone-sdk/01-architect/01-app-generation.md +183 -69
  4. package/templates/vite/.howone/skills/howone-sdk/01-architect/02-manifest-codegen.md +98 -23
  5. package/templates/vite/.howone/skills/howone-sdk/02-database/01-schema-design.md +463 -69
  6. package/templates/vite/.howone/skills/howone-sdk/02-database/02-schema-operations.md +366 -64
  7. package/templates/vite/.howone/skills/howone-sdk/02-database/03-data-access-patterns.md +204 -67
  8. package/templates/vite/.howone/skills/howone-sdk/02-database/04-query-dsl-and-responses.md +237 -0
  9. package/templates/vite/.howone/skills/howone-sdk/02-database/05-ai-persistence-patterns.md +372 -0
  10. package/templates/vite/.howone/skills/howone-sdk/03-sdk/01-client-setup.md +58 -36
  11. package/templates/vite/.howone/skills/howone-sdk/03-sdk/02-entity-operations.md +67 -0
  12. package/templates/vite/.howone/skills/howone-sdk/03-sdk/03-auth.md +267 -469
  13. package/templates/vite/.howone/skills/howone-sdk/03-sdk/04-react-integration.md +113 -322
  14. package/templates/vite/.howone/skills/howone-sdk/03-sdk/07-ai-action-calls.md +95 -48
  15. package/templates/vite/.howone/skills/howone-sdk/03-sdk/08-extension-boundaries.md +226 -0
  16. package/templates/vite/.howone/skills/howone-sdk/04-ai/01-ai-capability-architecture.md +205 -0
  17. package/templates/vite/.howone/skills/howone-sdk/04-ai/02-workflow-contract-rules.md +426 -0
  18. package/templates/vite/.howone/skills/howone-sdk/04-ai/03-ai-sdk-handoff.md +219 -0
  19. package/templates/vite/.howone/skills/howone-sdk/04-ai/04-service-capability-catalog.md +281 -0
  20. package/templates/vite/.howone/skills/howone-sdk/04-ai/05-workflow-operations.md +256 -0
  21. package/templates/vite/.howone/skills/howone-sdk/04-ai/06-ai-feature-playbooks.md +296 -0
  22. package/templates/vite/.howone/skills/howone-sdk/SKILL.md +83 -15
  23. package/templates/vite/.howone/skills/howone-sdk/agents/openai.yaml +2 -2
  24. package/templates/vite/package.json +1 -1
  25. package/templates/vite/src/lib/sdk.ts +3 -0
  26. package/templates/vite/.howone/skills/howone-sdk/04-ai/.gitkeep +0 -1
@@ -0,0 +1,426 @@
1
+ # Workflow Contract Rules
2
+
3
+ Use this reference when designing `capability.description`, `inputSchema`, `outputSchema`, and
4
+ `outputEntityName` for HowOne AI workflows.
5
+
6
+ These rules come from `docs/ai-worlfow-guide-schema.md`. Violating them can make the workflow
7
+ service reject the request or produce a workflow that the SDK cannot call reliably.
8
+
9
+ ## Contract Shape
10
+
11
+ ```json
12
+ {
13
+ "name": "summarizeDocument",
14
+ "description": "Reads an uploaded document and produces a concise summary highlighting the key points.",
15
+ "inputSchema": {
16
+ "type": "object",
17
+ "properties": {
18
+ "document_url": {
19
+ "type": "string",
20
+ "format": "uri",
21
+ "description": "Supabase Storage URL of the uploaded document."
22
+ },
23
+ "summary_length": {
24
+ "type": "string",
25
+ "description": "Desired summary length, e.g. short, medium, long, or a specific sentence count."
26
+ }
27
+ },
28
+ "required": ["document_url"]
29
+ },
30
+ "outputSchema": {
31
+ "type": "object",
32
+ "properties": {
33
+ "summary": {
34
+ "type": "string",
35
+ "description": "The generated summary in the same language as the source document."
36
+ }
37
+ },
38
+ "required": ["summary"]
39
+ },
40
+ "outputEntityName": "DocumentSummary"
41
+ }
42
+ ```
43
+
44
+ ## Loose JSON Schema
45
+
46
+ The workflow engine is agentic. Overly strict schemas reduce reliability.
47
+
48
+ Do:
49
+
50
+ - require only essential inputs;
51
+ - make non-essential options optional;
52
+ - use strings with good descriptions for open-ended user choices;
53
+ - use nested objects only when the product truly needs structured output;
54
+ - use `format: "uri"` for URLs.
55
+
56
+ Avoid:
57
+
58
+ - `minLength`, `maxLength`, `pattern` unless technically required;
59
+ - enum for open-ended values like tone, style, audience, mood;
60
+ - many required knobs that normal users do not understand;
61
+ - provider/model/tool names in schema fields.
62
+
63
+ Good:
64
+
65
+ ```json
66
+ {
67
+ "tone": {
68
+ "type": "string",
69
+ "description": "Desired writing tone, e.g. formal, casual, humorous, empathetic, professional."
70
+ }
71
+ }
72
+ ```
73
+
74
+ Bad:
75
+
76
+ ```json
77
+ {
78
+ "tone": {
79
+ "type": "string",
80
+ "enum": ["formal", "casual", "humorous"],
81
+ "minLength": 3,
82
+ "maxLength": 20
83
+ }
84
+ }
85
+ ```
86
+
87
+ Use enum only for closed domains such as `"daily" | "minute"` or known output modes.
88
+
89
+ ## URLs Only For Files
90
+
91
+ All file exchange uses cloud storage URLs.
92
+
93
+ Correct inputs:
94
+
95
+ ```json
96
+ {
97
+ "source_image_url": {
98
+ "type": "string",
99
+ "format": "uri",
100
+ "description": "Public URL of the source image to edit."
101
+ }
102
+ }
103
+ ```
104
+
105
+ Correct outputs:
106
+
107
+ ```json
108
+ {
109
+ "edited_image_url": {
110
+ "type": "string",
111
+ "format": "uri",
112
+ "description": "Public URL of the edited image."
113
+ }
114
+ }
115
+ ```
116
+
117
+ Forbidden:
118
+
119
+ - raw `File` / `Blob`;
120
+ - base64 content;
121
+ - `contentEncoding`;
122
+ - inline PDF/image/audio bytes;
123
+ - browser upload logic in workflow.
124
+
125
+ The app uploads first through `howone.upload.*`, then passes the URL to the workflow.
126
+
127
+ ## Output Minimalism
128
+
129
+ Every output field costs model attention. Only include what the product will render or persist.
130
+
131
+ Usually forbidden unless explicitly requested:
132
+
133
+ - `processing_time`, `timestamp`, `created_at`;
134
+ - `model_used`, `provider`, `version`;
135
+ - `file_size`, `mime_type`, `resolution`, `frame_rate`;
136
+ - `confidence_score`, `bounding_boxes`, `coordinates`;
137
+ - style/color/tone metadata when user only asked for an asset.
138
+
139
+ Examples:
140
+
141
+ | User asks | Good output | Avoid |
142
+ |---|---|---|
143
+ | Summarize a PDF | `summary` | `summary`, `word_count`, `reading_time` |
144
+ | Generate image | `generated_image_url` | `generated_image_url`, `model_used`, `color_palette` |
145
+ | OCR image | `extracted_text` | `extracted_text`, `confidence_score`, `bounding_boxes` |
146
+ | Generate video | `video_url` | `video_url`, `duration_seconds`, `frame_rate` |
147
+
148
+ If the app needs history metadata, put it in the entity schema, not workflow output.
149
+
150
+ ## Input / Output Names Must Not Overlap
151
+
152
+ Input and output property names share a routing namespace. Do not reuse names.
153
+
154
+ | Scenario | Bad | Good |
155
+ |---|---|---|
156
+ | Translation | input `text`, output `text` | input `source_text`, output `translated_text` |
157
+ | Summary | input `content`, output `content` | input `source_content`, output `summary` |
158
+ | Image edit | input `image_url`, output `image_url` | input `source_image_url`, output `edited_image_url` |
159
+ | Audio transcript | input `audio_url`, output `audio_url` | input `source_audio_url`, output `transcript_text` |
160
+
161
+ ## Description Says What, Not How
162
+
163
+ `capability.description` describes the user-visible outcome.
164
+
165
+ Good:
166
+
167
+ ```json
168
+ "description": "Searches the web for recent news on a topic and produces a structured briefing with source links."
169
+ ```
170
+
171
+ Bad:
172
+
173
+ ```json
174
+ "description": "First calls search_web, then summarizes articles with an LLM, then saves the result."
175
+ ```
176
+
177
+ Do not mention:
178
+
179
+ - internal tool names;
180
+ - step sequences;
181
+ - model/provider names;
182
+ - database writes;
183
+ - storage implementation details except URL inputs/outputs.
184
+
185
+ ## Output Language Must Be Explicit
186
+
187
+ Every text output field description must say what language to use.
188
+
189
+ Patterns:
190
+
191
+ ```json
192
+ "summary": {
193
+ "type": "string",
194
+ "description": "Summary in the same language as the source document."
195
+ }
196
+ ```
197
+
198
+ ```json
199
+ "translated_text": {
200
+ "type": "string",
201
+ "description": "Translated text in the target language specified by 'target_language'."
202
+ }
203
+ ```
204
+
205
+ ```json
206
+ "briefing": {
207
+ "type": "string",
208
+ "description": "Briefing written in the language specified by 'language'."
209
+ }
210
+ ```
211
+
212
+ Do not write vague descriptions like `"The translated text."`
213
+
214
+ ## No CRUD In Workflow
215
+
216
+ Workflow belongs to intelligent processing. App persistence belongs to entities.
217
+
218
+ Forbidden in capability descriptions and schemas:
219
+
220
+ - "save to database";
221
+ - "create user record";
222
+ - "update task status";
223
+ - "delete previous result";
224
+ - "read current user's records";
225
+ - "assign owner".
226
+
227
+ Instead:
228
+
229
+ 1. workflow returns output fields;
230
+ 2. frontend/server code calls SDK entity methods;
231
+ 3. persistence helper maps durable fields.
232
+
233
+ ## External Data Assumptions
234
+
235
+ Do not require user-provided datasets unless the user said they have them.
236
+
237
+ | User asks | Bad input | Better input |
238
+ |---|---|---|
239
+ | Stock analysis app | `stock_data_csv_url` | `trading_symbol`, `start_date`, `end_date` |
240
+ | Latest news app | `article_urls` | `topic`, `language` |
241
+ | Academic research | `paper_pdf_urls` | `query`, optional `year_range` |
242
+ | Product comparison | `product_dataset_url` | `product_names` or `search_topic` |
243
+
244
+ The workflow should use available retrieval/search capabilities when data is external.
245
+
246
+ ## Raw JSON Requirement
247
+
248
+ Schemas must be raw JSON objects:
249
+
250
+ - start with `{` and end with `}`;
251
+ - ASCII double quotes;
252
+ - no trailing commas;
253
+ - no `undefined`, `NaN`, comments, or single quotes;
254
+ - not a string containing escaped JSON.
255
+
256
+ Good:
257
+
258
+ ```json
259
+ { "type": "object", "properties": {} }
260
+ ```
261
+
262
+ Bad:
263
+
264
+ ```json
265
+ "{ \"type\": \"object\" }"
266
+ ```
267
+
268
+ ## Schema Pattern Templates
269
+
270
+ ### Text generation
271
+
272
+ ```json
273
+ {
274
+ "inputSchema": {
275
+ "type": "object",
276
+ "properties": {
277
+ "topic": {
278
+ "type": "string",
279
+ "description": "Topic to write about."
280
+ },
281
+ "audience": {
282
+ "type": "string",
283
+ "description": "Intended audience, e.g. executives, children, developers, or general readers."
284
+ },
285
+ "language": {
286
+ "type": "string",
287
+ "description": "Output language, e.g. English or Chinese."
288
+ }
289
+ },
290
+ "required": ["topic"]
291
+ },
292
+ "outputSchema": {
293
+ "type": "object",
294
+ "properties": {
295
+ "generated_text": {
296
+ "type": "string",
297
+ "description": "Generated text in the language specified by 'language', or English if not specified."
298
+ }
299
+ },
300
+ "required": ["generated_text"]
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### Image generation
306
+
307
+ ```json
308
+ {
309
+ "inputSchema": {
310
+ "type": "object",
311
+ "properties": {
312
+ "image_description": {
313
+ "type": "string",
314
+ "description": "Detailed description of the image to generate, including subject, style, mood, and composition."
315
+ },
316
+ "style_preference": {
317
+ "type": "string",
318
+ "description": "Optional style preference such as watercolor, pixel art, photorealistic, anime, or flat design."
319
+ }
320
+ },
321
+ "required": ["image_description"]
322
+ },
323
+ "outputSchema": {
324
+ "type": "object",
325
+ "properties": {
326
+ "generated_image_url": {
327
+ "type": "string",
328
+ "format": "uri",
329
+ "description": "Public URL of the generated image."
330
+ }
331
+ },
332
+ "required": ["generated_image_url"]
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### Image editing
338
+
339
+ ```json
340
+ {
341
+ "inputSchema": {
342
+ "type": "object",
343
+ "properties": {
344
+ "source_image_url": {
345
+ "type": "string",
346
+ "format": "uri",
347
+ "description": "Public URL of the image to edit."
348
+ },
349
+ "edit_instruction": {
350
+ "type": "string",
351
+ "description": "Natural language instruction describing the desired edit."
352
+ }
353
+ },
354
+ "required": ["source_image_url", "edit_instruction"]
355
+ },
356
+ "outputSchema": {
357
+ "type": "object",
358
+ "properties": {
359
+ "edited_image_url": {
360
+ "type": "string",
361
+ "format": "uri",
362
+ "description": "Public URL of the edited image."
363
+ }
364
+ },
365
+ "required": ["edited_image_url"]
366
+ }
367
+ }
368
+ ```
369
+
370
+ ### Research briefing
371
+
372
+ ```json
373
+ {
374
+ "inputSchema": {
375
+ "type": "object",
376
+ "properties": {
377
+ "topic": {
378
+ "type": "string",
379
+ "description": "Research topic or question."
380
+ },
381
+ "language": {
382
+ "type": "string",
383
+ "description": "Language for the output briefing."
384
+ }
385
+ },
386
+ "required": ["topic"]
387
+ },
388
+ "outputSchema": {
389
+ "type": "object",
390
+ "properties": {
391
+ "briefing": {
392
+ "type": "object",
393
+ "description": "Structured briefing written in the language specified by 'language'.",
394
+ "properties": {
395
+ "summary": { "type": "string" },
396
+ "sources": {
397
+ "type": "array",
398
+ "items": {
399
+ "type": "object",
400
+ "properties": {
401
+ "title": { "type": "string" },
402
+ "url": { "type": "string", "format": "uri" },
403
+ "key_point": { "type": "string" }
404
+ }
405
+ }
406
+ }
407
+ },
408
+ "required": ["summary", "sources"]
409
+ }
410
+ },
411
+ "required": ["briefing"]
412
+ }
413
+ }
414
+ ```
415
+
416
+ ## Contract Checklist
417
+
418
+ - Required inputs are essential only.
419
+ - File inputs/outputs are URL strings.
420
+ - Output contains only the requested product result.
421
+ - Input/output property names do not overlap.
422
+ - Text output descriptions specify language.
423
+ - Description says what, not how.
424
+ - No CRUD/auth/upload/payment/app-state requirements.
425
+ - Feature fits an available capability.
426
+ - Schema is raw valid JSON.
@@ -0,0 +1,219 @@
1
+ # AI SDK Handoff
2
+
3
+ Use this reference after AI capability artifacts have been synced and app code must call the
4
+ workflow through `@howone/sdk`.
5
+
6
+ This file answers: **how does `.howone/ai/manifest.json` become `src/lib/sdk.ts`, and how should UI
7
+ call it?**
8
+
9
+ ## Binding Source
10
+
11
+ Generate `src/lib/sdk.ts` from `.howone/ai/manifest.json`. Do not write AI bindings from memory,
12
+ from the original prompt, or from the workflow service response.
13
+
14
+ For each manifest capability/action:
15
+
16
+ 1. Read stable action name/ID.
17
+ 2. Read `workflowId`.
18
+ 3. Read `inputSchema`.
19
+ 4. Read `outputSchema`.
20
+ 5. Generate Zod input and output schemas.
21
+ 6. Bind with `defineAiAction(name, { workflowId, inputSchema, outputSchema })`.
22
+ 7. Compose with `withAiActions(client, ai)`.
23
+
24
+ `workflowId` is mandatory. Without it, the SDK falls back to the action name as the execution URL
25
+ segment, and the workflow service will reject it because the segment is not a UUID.
26
+
27
+ ## Generated Binding Example
28
+
29
+ ```ts
30
+ import {
31
+ createClient,
32
+ defineAiAction,
33
+ defineAiActions,
34
+ withAiActions,
35
+ } from '@howone/sdk'
36
+ import { z } from 'zod'
37
+
38
+ const client = createClient({
39
+ projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
40
+ env: import.meta.env.VITE_HOWONE_ENV,
41
+ })
42
+
43
+ export const summarizeDocumentInputSchema = z.object({
44
+ document_url: z.string().url(),
45
+ summary_length: z.string().optional(),
46
+ })
47
+ export type SummarizeDocumentInput = z.infer<typeof summarizeDocumentInputSchema>
48
+
49
+ export const summarizeDocumentOutputSchema = z.object({
50
+ summary: z.string(),
51
+ })
52
+ export type SummarizeDocumentOutput = z.infer<typeof summarizeDocumentOutputSchema>
53
+
54
+ export const ai = defineAiActions({
55
+ summarizeDocument: defineAiAction('summarizeDocument', {
56
+ workflowId: '550e8400-e29b-41d4-a716-446655440000',
57
+ inputSchema: summarizeDocumentInputSchema,
58
+ outputSchema: summarizeDocumentOutputSchema,
59
+ }),
60
+ })
61
+
62
+ const howone = withAiActions(client, ai)
63
+ export default howone
64
+ ```
65
+
66
+ ## JSON Schema To Zod
67
+
68
+ | JSON Schema | Zod |
69
+ |---|---|
70
+ | `string` | `z.string()` |
71
+ | `string` + `format: "uri"` | `z.string().url()` |
72
+ | `number` | `z.number()` |
73
+ | `integer` | `z.number().int()` |
74
+ | `boolean` | `z.boolean()` |
75
+ | `array` of strings | `z.array(z.string())` |
76
+ | `array` of objects | `z.array(z.object({ ... }))` |
77
+ | `object` | `z.object({ ... })` |
78
+ | string enum | `z.enum([...])` |
79
+ | field not in `required` | `.optional()` |
80
+ | nullable | `.nullable()` |
81
+
82
+ Rules:
83
+
84
+ - Required manifest fields must stay required in Zod.
85
+ - Do not add `.passthrough()` to hide execution envelope problems.
86
+ - Do not make outputs optional to silence validation failures.
87
+ - If the workflow returns a different shape, fix the workflow/capability contract.
88
+
89
+ ## Calling Actions
90
+
91
+ For typed one-shot actions:
92
+
93
+ ```ts
94
+ const output = await howone.ai.summarizeDocument.run({
95
+ document_url,
96
+ summary_length: 'short',
97
+ })
98
+
99
+ setSummary(output.summary)
100
+ ```
101
+
102
+ When `outputSchema` exists, `.run()` returns the validated `finalResult` payload directly.
103
+
104
+ Do not read:
105
+
106
+ ```ts
107
+ result.finalResult.summary
108
+ result.data.summary
109
+ result.raw.finalResult
110
+ ```
111
+
112
+ Those are execution-envelope paths, not the typed SDK action contract.
113
+
114
+ ## Streaming And Events
115
+
116
+ Use `.stream()` when UI needs live output or cancellation:
117
+
118
+ ```ts
119
+ const session = howone.ai.generateStory.stream(input, {
120
+ onStreamChunk: (chunk) => setDraft((prev) => prev + chunk),
121
+ onProgress: (progress) => setProgress(progress),
122
+ onError: (error) => setError(error.message),
123
+ onComplete: (result) => setRawResult(result),
124
+ })
125
+
126
+ cancelButton.onclick = () => session.cancel()
127
+ const final = await session.result
128
+ ```
129
+
130
+ Use `.events()` when code wants an async iterable:
131
+
132
+ ```ts
133
+ for await (const event of howone.ai.generateStory.events(input)) {
134
+ if (event.type === 'stream_content') {
135
+ appendText(String(event.data?.delta ?? ''))
136
+ }
137
+ }
138
+ ```
139
+
140
+ ## UI State
141
+
142
+ The SDK returns data and exposes callbacks. The app owns all visible UI.
143
+
144
+ Recommended states:
145
+
146
+ ```ts
147
+ type AiUiState<T> =
148
+ | { status: 'idle' }
149
+ | { status: 'running'; progress?: number }
150
+ | { status: 'succeeded'; output: T }
151
+ | { status: 'failed'; message: string }
152
+ | { status: 'cancelled' }
153
+ ```
154
+
155
+ Do not add or import SDK toast APIs. Do not show SDK-owned overlays.
156
+
157
+ ## Persistence Handoff
158
+
159
+ If AI output should survive refresh, use entity persistence after the action returns.
160
+
161
+ For history-style products, prefer `runAiActionAndPersist()`:
162
+
163
+ ```ts
164
+ const result = await runAiActionAndPersist({
165
+ entity: howone.entities.Generation,
166
+ input: { prompt },
167
+ createPending: (input) => ({
168
+ prompt: input.prompt,
169
+ status: 'pending',
170
+ requestedAt: new Date().toISOString(),
171
+ }),
172
+ run: (input) => howone.ai.generateImage.run(input),
173
+ mapCompleted: ({ output }) => ({
174
+ status: 'completed',
175
+ resultUrl: output.generated_image_url,
176
+ completedAt: new Date().toISOString(),
177
+ }),
178
+ mapFailed: ({ error }) => ({
179
+ status: 'failed',
180
+ errorMessage: error instanceof Error ? error.message : 'Generation failed',
181
+ }),
182
+ })
183
+ ```
184
+
185
+ For simple save-after-success:
186
+
187
+ ```ts
188
+ const output = await howone.ai.summarizeDocument.run(input)
189
+ await howone.entities.DocumentSummary.create({
190
+ documentUrl: input.document_url,
191
+ summary: output.summary,
192
+ status: 'completed',
193
+ })
194
+ ```
195
+
196
+ Do not ask the workflow to write records. Do not pass owner fields for authenticated own entities.
197
+
198
+ ## Workflow Edit Handoff
199
+
200
+ When changing external workflow behavior later:
201
+
202
+ 1. If schema changes, update AI capability contract first and sync manifest.
203
+ 2. Use `workflowConfigID` from a completed status result.
204
+ 3. Submit update with `capabilityName`, `workflowConfigID`, and `updatePrompt`.
205
+ 4. Preserve the new status result and config ID if it changes.
206
+ 5. Regenerate SDK only if manifest contract changed.
207
+
208
+ `workflowConfigID` is not `workflowId`.
209
+
210
+ ## Handoff Checklist
211
+
212
+ - `.howone/ai/manifest.json` exists and is current.
213
+ - Each action has `workflowId`.
214
+ - Zod input/output schemas match manifest required fields.
215
+ - `defineAiAction` uses action name + exact workflow UUID.
216
+ - UI uses returned typed output, not raw execution envelope.
217
+ - Streaming session is cancellable when UI exposes cancel.
218
+ - Persistence goes through `howone.entities.*`.
219
+ - Visible status/error UI is app-owned.