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.
- package/package.json +1 -1
- package/templates/nextjs/lib/sdk.ts +3 -0
- package/templates/vite/.howone/skills/howone-sdk/01-architect/01-app-generation.md +183 -69
- package/templates/vite/.howone/skills/howone-sdk/01-architect/02-manifest-codegen.md +98 -23
- package/templates/vite/.howone/skills/howone-sdk/02-database/01-schema-design.md +463 -69
- package/templates/vite/.howone/skills/howone-sdk/02-database/02-schema-operations.md +366 -64
- package/templates/vite/.howone/skills/howone-sdk/02-database/03-data-access-patterns.md +204 -67
- package/templates/vite/.howone/skills/howone-sdk/02-database/04-query-dsl-and-responses.md +237 -0
- package/templates/vite/.howone/skills/howone-sdk/02-database/05-ai-persistence-patterns.md +372 -0
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/01-client-setup.md +58 -36
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/02-entity-operations.md +67 -0
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/03-auth.md +267 -469
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/04-react-integration.md +113 -322
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/07-ai-action-calls.md +95 -48
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/08-extension-boundaries.md +226 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/01-ai-capability-architecture.md +205 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/02-workflow-contract-rules.md +426 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/03-ai-sdk-handoff.md +219 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/04-service-capability-catalog.md +281 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/05-workflow-operations.md +256 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/06-ai-feature-playbooks.md +296 -0
- package/templates/vite/.howone/skills/howone-sdk/SKILL.md +83 -15
- package/templates/vite/.howone/skills/howone-sdk/agents/openai.yaml +2 -2
- package/templates/vite/package.json +1 -1
- package/templates/vite/src/lib/sdk.ts +3 -0
- 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.
|