howone 0.1.22 → 0.1.23
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 +27 -2
- package/templates/vite/.howone/skills/howone-sdk/01-architect/02-manifest-codegen.md +31 -19
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/04-react-integration.md +2 -4
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/07-ai-action-calls.md +31 -34
- package/templates/vite/.howone/skills/howone-sdk/04-ai/01-ai-capability-architecture.md +142 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/02-workflow-contract-rules.md +169 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/03-ai-sdk-handoff.md +80 -0
- package/templates/vite/.howone/skills/howone-sdk/SKILL.md +60 -9
- package/templates/vite/src/lib/sdk.ts +3 -0
package/package.json
CHANGED
|
@@ -18,6 +18,9 @@ export const entities = defineEntities({
|
|
|
18
18
|
|
|
19
19
|
export const ai = defineAiActions({
|
|
20
20
|
// Add generated AI action bindings here, for example:
|
|
21
|
+
// import { z } from "zod" and define Zod schemas before using defineAiAction.
|
|
22
|
+
// Do not paste JSON Schema objects from .howone/ai/manifest.json here directly.
|
|
23
|
+
// With outputSchema configured, howone.ai.<action>.run() returns the validated finalResult payload.
|
|
21
24
|
// generateImage: defineAiAction("generateImage", {
|
|
22
25
|
// inputSchema: generateImageInputSchema,
|
|
23
26
|
// outputSchema: generateImageOutputSchema,
|
|
@@ -9,8 +9,10 @@ HowOne generated apps have three layers:
|
|
|
9
9
|
|
|
10
10
|
1. **Backend contract**: dynamic entities, access rules, indexes, schema versions, and synced
|
|
11
11
|
manifests.
|
|
12
|
-
2. **
|
|
13
|
-
|
|
12
|
+
2. **AI contract + workflow implementation**: AI capability contracts are versioned by HowOne;
|
|
13
|
+
external workflow create/update is submitted from synced AI manifests.
|
|
14
|
+
3. **SDK binding**: `src/lib/sdk.ts` converts manifests into typed entity and AI clients.
|
|
15
|
+
4. **Frontend experience**: React/UI code calls the SDK and owns app-specific interaction design.
|
|
14
16
|
|
|
15
17
|
Do not skip the binding layer. The UI should call `src/lib/sdk.ts`, not raw entity names guessed
|
|
16
18
|
from the user prompt.
|
|
@@ -60,6 +62,29 @@ When a request adds persistence or changes data shape:
|
|
|
60
62
|
Do not preview a patch and then execute different single operations. Related schema changes for
|
|
61
63
|
one feature should be grouped into one preview/apply cycle.
|
|
62
64
|
|
|
65
|
+
## AI Feature Workflow
|
|
66
|
+
|
|
67
|
+
When a request adds an AI capability or changes an AI workflow:
|
|
68
|
+
|
|
69
|
+
1. Read `04-ai/01-ai-capability-architecture.md` and
|
|
70
|
+
`04-ai/02-workflow-contract-rules.md`.
|
|
71
|
+
2. Inspect current AI capability state.
|
|
72
|
+
3. Design one complete AI capability patch for the feature.
|
|
73
|
+
4. Preview the AI patch.
|
|
74
|
+
5. Apply the exact previewed operations.
|
|
75
|
+
6. Sync AI artifacts.
|
|
76
|
+
7. Read `.howone/ai/manifest.json`.
|
|
77
|
+
8. Submit external workflow create/update with `external-ai-capability`.
|
|
78
|
+
9. Preserve returned request IDs for the status/background-task layer.
|
|
79
|
+
10. Read `04-ai/03-ai-sdk-handoff.md`, `01-architect/02-manifest-codegen.md`, and
|
|
80
|
+
`03-sdk/07-ai-action-calls.md`.
|
|
81
|
+
11. Update `src/lib/sdk.ts` from the synced manifest.
|
|
82
|
+
12. Update the UI.
|
|
83
|
+
13. Validate.
|
|
84
|
+
|
|
85
|
+
If generated output must be saved, design the AI capability first. Then derive persistence entities
|
|
86
|
+
from the synced AI `outputSchema`, plus only necessary request metadata.
|
|
87
|
+
|
|
63
88
|
## Auth Decision
|
|
64
89
|
|
|
65
90
|
Use hosted auth when the app just needs login:
|
|
@@ -13,6 +13,11 @@ HowOne apps are driven by two backend-synced manifests:
|
|
|
13
13
|
|
|
14
14
|
Sync tools (`sync_schema_artifacts`, `sync_ai_artifacts`) write the manifests. The coding agent reads the manifests and writes `src/lib/sdk.ts`.
|
|
15
15
|
|
|
16
|
+
For AI capabilities, external workflow create/update is submitted by `external-ai-capability` from
|
|
17
|
+
the synced manifest. Do not duplicate AI schemas in app code beyond generated zod/type bindings.
|
|
18
|
+
For workflow edits, `workflowConfigID` belongs to the external workflow operation; it is not an SDK
|
|
19
|
+
binding field.
|
|
20
|
+
|
|
16
21
|
---
|
|
17
22
|
|
|
18
23
|
## Reading `.howone/database/manifest.json`
|
|
@@ -157,6 +162,7 @@ export type CommentUpdate = Partial<CommentCreate>
|
|
|
157
162
|
{
|
|
158
163
|
"id": "generateStory",
|
|
159
164
|
"name": "Generate Story",
|
|
165
|
+
"workflowId": "d69ab648-2c00-4d94-928e-01bd7b2a5bb2",
|
|
160
166
|
"inputSchema": {
|
|
161
167
|
"type": "object",
|
|
162
168
|
"properties": {
|
|
@@ -179,6 +185,7 @@ export type CommentUpdate = Partial<CommentCreate>
|
|
|
179
185
|
{
|
|
180
186
|
"id": "translateText",
|
|
181
187
|
"name": "Translate Text",
|
|
188
|
+
"workflowId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
182
189
|
"inputSchema": {
|
|
183
190
|
"type": "object",
|
|
184
191
|
"properties": {
|
|
@@ -219,12 +226,12 @@ export const generateStoryInputSchema = z.object({
|
|
|
219
226
|
})
|
|
220
227
|
export type GenerateStoryInput = z.infer<typeof generateStoryInputSchema>
|
|
221
228
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
229
|
+
export const generateStoryOutputSchema = z.object({
|
|
230
|
+
title: z.string(),
|
|
231
|
+
content: z.string(),
|
|
232
|
+
summary: z.string().optional(),
|
|
233
|
+
})
|
|
234
|
+
export type GenerateStoryOutput = z.infer<typeof generateStoryOutputSchema>
|
|
228
235
|
|
|
229
236
|
// ── translateText ─────────────────────────────────────────────
|
|
230
237
|
export const translateTextInputSchema = z.object({
|
|
@@ -235,6 +242,10 @@ export const translateTextInputSchema = z.object({
|
|
|
235
242
|
export type TranslateTextInput = z.infer<typeof translateTextInputSchema>
|
|
236
243
|
```
|
|
237
244
|
|
|
245
|
+
Do not make required output fields optional to silence validation failures. Do not add
|
|
246
|
+
`.passthrough()` as a workaround for EAX execution envelopes. `defineAiAction` validates the
|
|
247
|
+
workflow `finalResult` payload when an `outputSchema` is configured.
|
|
248
|
+
|
|
238
249
|
---
|
|
239
250
|
|
|
240
251
|
## Full Generated `src/lib/sdk.ts`
|
|
@@ -303,11 +314,12 @@ export const generateStoryInputSchema = z.object({
|
|
|
303
314
|
language: z.string().optional(),
|
|
304
315
|
})
|
|
305
316
|
export type GenerateStoryInput = z.infer<typeof generateStoryInputSchema>
|
|
306
|
-
export
|
|
307
|
-
title: string
|
|
308
|
-
content: string
|
|
309
|
-
summary
|
|
310
|
-
}
|
|
317
|
+
export const generateStoryOutputSchema = z.object({
|
|
318
|
+
title: z.string(),
|
|
319
|
+
content: z.string(),
|
|
320
|
+
summary: z.string().optional(),
|
|
321
|
+
})
|
|
322
|
+
export type GenerateStoryOutput = z.infer<typeof generateStoryOutputSchema>
|
|
311
323
|
|
|
312
324
|
export const translateTextInputSchema = z.object({
|
|
313
325
|
text: z.string(),
|
|
@@ -315,10 +327,6 @@ export const translateTextInputSchema = z.object({
|
|
|
315
327
|
formality: z.enum(['formal', 'informal']).optional(),
|
|
316
328
|
})
|
|
317
329
|
export type TranslateTextInput = z.infer<typeof translateTextInputSchema>
|
|
318
|
-
export type TranslateTextOutput = {
|
|
319
|
-
translatedText: string
|
|
320
|
-
detectedLang?: string
|
|
321
|
-
}
|
|
322
330
|
|
|
323
331
|
// ═══════════════════════════════════════════════════════════════
|
|
324
332
|
// CLIENT
|
|
@@ -344,9 +352,12 @@ export const entities = defineEntities({
|
|
|
344
352
|
|
|
345
353
|
export const ai = defineAiActions({
|
|
346
354
|
generateStory: defineAiAction('generateStory', {
|
|
355
|
+
workflowId: 'd69ab648-2c00-4d94-928e-01bd7b2a5bb2',
|
|
347
356
|
inputSchema: generateStoryInputSchema,
|
|
357
|
+
outputSchema: generateStoryOutputSchema,
|
|
348
358
|
}),
|
|
349
359
|
translateText: defineAiAction('translateText', {
|
|
360
|
+
workflowId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
350
361
|
inputSchema: translateTextInputSchema,
|
|
351
362
|
}),
|
|
352
363
|
})
|
|
@@ -371,7 +382,11 @@ Before finalising generated code, verify:
|
|
|
371
382
|
- [ ] System fields are not present in create/update input types
|
|
372
383
|
- [ ] Public query/write types are generated only from `access.public.allowedFilters`, `allowedSorts`, `requiredScopes`, and write permissions
|
|
373
384
|
- [ ] Every AI action from `.howone/ai/manifest.json` has an `inputSchema` zod object
|
|
385
|
+
- [ ] Every AI action with manifest `outputSchema` has a matching zod `outputSchema`
|
|
386
|
+
- [ ] Every AI action binding includes the exact manifest `workflowId`
|
|
374
387
|
- [ ] Required input fields are not `.optional()` in zod
|
|
388
|
+
- [ ] Required output fields are not `.optional()` in zod
|
|
389
|
+
- [ ] AI output schemas do not use `.passthrough()` to hide execution-envelope mismatches
|
|
375
390
|
- [ ] AI action names match the manifest `id` exactly (case-sensitive)
|
|
376
391
|
- [ ] `createClient` uses `import.meta.env.*` only
|
|
377
392
|
- [ ] `withEntities` is applied before `withAiActions` in the composition chain
|
|
@@ -418,10 +433,7 @@ When the app generates data with AI and saves it to an entity:
|
|
|
418
433
|
// Save it to Story entity which adds: authorId, status, wordCount
|
|
419
434
|
|
|
420
435
|
async function generateAndSave(input: GenerateStoryInput, authorId: string) {
|
|
421
|
-
const
|
|
422
|
-
if (!result.success) throw new Error(result.errors.join(', '))
|
|
423
|
-
|
|
424
|
-
const output = result.finalResult as GenerateStoryOutput
|
|
436
|
+
const output = await howone.ai.generateStory.run(input)
|
|
425
437
|
|
|
426
438
|
return howone.entities.Story.create({
|
|
427
439
|
title: output.title,
|
|
@@ -368,10 +368,8 @@ function AIGenerateButton({ topic }: { topic: string }) {
|
|
|
368
368
|
}
|
|
369
369
|
setLoading(true)
|
|
370
370
|
try {
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
setResult((res.finalResult as any).content)
|
|
374
|
-
}
|
|
371
|
+
const output = await howone.ai.generateStory.run({ topic, language: 'en' })
|
|
372
|
+
setResult(output.content)
|
|
375
373
|
} finally {
|
|
376
374
|
setLoading(false)
|
|
377
375
|
}
|
|
@@ -4,24 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
**`src/lib/sdk.ts` must be generated from `.howone/ai/manifest.json`. Do not write it from memory or from generic examples.**
|
|
6
6
|
|
|
7
|
+
For AI capability and workflow design, read `04-ai/` first. This file is only for app-side SDK
|
|
8
|
+
bindings and runtime calls after the manifest exists.
|
|
9
|
+
|
|
7
10
|
For every capability in `manifest.json`:
|
|
8
11
|
1. Read `name`, `workflowId`, `inputSchema`, `outputSchema`
|
|
9
12
|
2. Generate a zod schema from `inputSchema.properties`
|
|
10
|
-
3.
|
|
13
|
+
3. Generate a zod schema from `outputSchema.properties` when an output schema exists
|
|
14
|
+
4. Call `defineAiAction(name, { workflowId, inputSchema, outputSchema })` — **`workflowId` is mandatory**
|
|
11
15
|
|
|
12
16
|
Without `workflowId`, the SDK falls back to using the action name as the URL segment. Action names are not UUIDs — the EAX server will reject the call with "invalid input syntax for type uuid".
|
|
13
17
|
|
|
18
|
+
Do not mark required manifest output fields as `.optional()` to silence validation. Do not add
|
|
19
|
+
`.passthrough()` as a workaround for execution envelopes. A typed `run()` validates and returns the
|
|
20
|
+
workflow `finalResult` payload, not the raw execution envelope.
|
|
21
|
+
|
|
14
22
|
## When to Write SDK Bindings
|
|
15
23
|
|
|
16
|
-
**Do NOT write `defineAiAction` until `.howone/ai/manifest.json` contains
|
|
24
|
+
**Do NOT write `defineAiAction` until `.howone/ai/manifest.json` contains the workflowId for the capability and the external workflow implementation has been submitted/confirmed by the workflow layer.**
|
|
17
25
|
|
|
18
26
|
Correct sequence:
|
|
19
|
-
1. `
|
|
27
|
+
1. `ai-capability-design` — design the capability contract
|
|
20
28
|
2. `sync_ai_artifacts` — sync manifest to disk
|
|
21
|
-
3. `
|
|
22
|
-
4.
|
|
23
|
-
5. `
|
|
24
|
-
6. Read `.howone/ai/manifest.json` → write `src/lib/sdk.ts` with `workflowId`
|
|
29
|
+
3. `external-ai-capability` — submit workflow create/update to EAX from the synced manifest
|
|
30
|
+
4. Workflow status/background-task layer confirms readiness and captures `workflowConfigID` for future edits
|
|
31
|
+
5. Read `.howone/ai/manifest.json` → write `src/lib/sdk.ts` with `workflowId`
|
|
25
32
|
|
|
26
33
|
Building without errors does **not** mean the AI workflow binding is correct. A missing `workflowId` causes a runtime UUID error at the EAX execution call.
|
|
27
34
|
|
|
@@ -45,7 +52,7 @@ Building without errors does **not** mean the AI workflow binding is correct. A
|
|
|
45
52
|
type AiActionConfig<TInput, TOutput> = {
|
|
46
53
|
workflowId: string // REQUIRED — UUID from manifest.json. Without this, SDK uses action name as URL segment (not a UUID → EAX rejects).
|
|
47
54
|
inputSchema?: z.ZodType<TInput> // validates input before calling the workflow
|
|
48
|
-
outputSchema?: z.ZodType<TOutput> // validates the
|
|
55
|
+
outputSchema?: z.ZodType<TOutput> // validates the workflow finalResult payload for run()
|
|
49
56
|
mode?: 'run' | 'stream' | 'events' // default: supports all three modes
|
|
50
57
|
}
|
|
51
58
|
```
|
|
@@ -120,7 +127,7 @@ export const ai = defineAiActions({
|
|
|
120
127
|
})
|
|
121
128
|
```
|
|
122
129
|
|
|
123
|
-
### Action with typed output
|
|
130
|
+
### Action with typed output
|
|
124
131
|
|
|
125
132
|
```ts
|
|
126
133
|
export const generateStoryOutputSchema = z.object({
|
|
@@ -130,13 +137,11 @@ export const generateStoryOutputSchema = z.object({
|
|
|
130
137
|
})
|
|
131
138
|
export type GenerateStoryOutput = z.infer<typeof generateStoryOutputSchema>
|
|
132
139
|
|
|
133
|
-
// Do NOT put outputSchema in defineAiAction unless it matches the full
|
|
134
|
-
// AiResult envelope. Instead, cast finalResult after run():
|
|
135
140
|
export const ai = defineAiActions({
|
|
136
141
|
generateStory: defineAiAction('generateStory', {
|
|
137
142
|
workflowId: 'd69ab648-2c00-4d94-928e-01bd7b2a5bb2', // ← from manifest.json
|
|
138
143
|
inputSchema: generateStoryInputSchema,
|
|
139
|
-
|
|
144
|
+
outputSchema: generateStoryOutputSchema,
|
|
140
145
|
}),
|
|
141
146
|
})
|
|
142
147
|
```
|
|
@@ -171,25 +176,21 @@ export const ai = defineAiActions({
|
|
|
171
176
|
|
|
172
177
|
## Calling AI Actions
|
|
173
178
|
|
|
174
|
-
### run() —
|
|
179
|
+
### run() — typed action result
|
|
175
180
|
|
|
176
181
|
```ts
|
|
177
182
|
import howone, { type GenerateStoryInput, type GenerateStoryOutput } from '@/lib/sdk'
|
|
178
183
|
|
|
179
184
|
async function generateStory(input: GenerateStoryInput) {
|
|
180
|
-
const
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
if (!result.success) {
|
|
184
|
-
throw new Error(result.errors.join(', '))
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Cast finalResult to your output type
|
|
188
|
-
const output = result.finalResult as GenerateStoryOutput
|
|
185
|
+
const output = await howone.ai.generateStory.run(input)
|
|
186
|
+
// output is GenerateStoryOutput when outputSchema is configured.
|
|
189
187
|
return output
|
|
190
188
|
}
|
|
191
189
|
```
|
|
192
190
|
|
|
191
|
+
When an action has `outputSchema`, `run()` returns the validated workflow `finalResult` payload.
|
|
192
|
+
When an action omits `outputSchema`, `run()` returns the raw `AiResult` execution envelope.
|
|
193
|
+
|
|
193
194
|
### run() — with SSE callbacks
|
|
194
195
|
|
|
195
196
|
```ts
|
|
@@ -362,7 +363,7 @@ type SSEExecutionOptions = {
|
|
|
362
363
|
// Called if an error occurs
|
|
363
364
|
onError?: (error: Error) => void
|
|
364
365
|
|
|
365
|
-
// Called when the workflow completes
|
|
366
|
+
// Called when the workflow completes with the raw execution envelope
|
|
366
367
|
onComplete?: (result: AiResult) => void
|
|
367
368
|
|
|
368
369
|
// Abort signal — connect to an AbortController for cancellation
|
|
@@ -406,15 +407,11 @@ When AI-generated content should be saved to an entity:
|
|
|
406
407
|
|
|
407
408
|
```ts
|
|
408
409
|
// 1. Run the AI action
|
|
409
|
-
const
|
|
410
|
+
const output = await howone.ai.generateStory.run({
|
|
410
411
|
topic: 'Dragons and magic',
|
|
411
412
|
ageRange: '6-8',
|
|
412
413
|
})
|
|
413
414
|
|
|
414
|
-
if (!result.success) throw new Error(result.errors.join(', '))
|
|
415
|
-
|
|
416
|
-
const output = result.finalResult as GenerateStoryOutput
|
|
417
|
-
|
|
418
415
|
// 2. Save to entity
|
|
419
416
|
const saved = await howone.entities.Story.create({
|
|
420
417
|
title: output.title,
|
|
@@ -448,9 +445,8 @@ function GenerateStoryButton({ input }: { input: GenerateStoryInput }) {
|
|
|
448
445
|
setLoading(true)
|
|
449
446
|
setError(null)
|
|
450
447
|
try {
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
setResult(res.finalResult as GenerateStoryOutput)
|
|
448
|
+
const output = await howone.ai.generateStory.run(input)
|
|
449
|
+
setResult(output)
|
|
454
450
|
} catch (err) {
|
|
455
451
|
if (err instanceof AiSchemaValidationError) {
|
|
456
452
|
setError(`Validation error: ${err.issues.map(i => i.message).join(', ')}`)
|
|
@@ -522,10 +518,11 @@ function StreamingStoryGenerator({ input }: { input: GenerateStoryInput }) {
|
|
|
522
518
|
| Mistake | Correct Pattern |
|
|
523
519
|
|---|---|
|
|
524
520
|
| `defineAiAction('generateStory', { inputSchema })` — no `workflowId` | Always include `workflowId` from `manifest.json`; SDK falls back to action name, which is not a UUID → EAX rejects |
|
|
525
|
-
| Writing `src/lib/sdk.ts` before `
|
|
521
|
+
| Writing `src/lib/sdk.ts` before `.howone/ai/manifest.json` has a workflowId | Run `ai-capability-design` → `sync_ai_artifacts` → `external-ai-capability`; only write bindings from the synced manifest |
|
|
526
522
|
| Hardcoding `workflowId` from memory or guessing | Always read from `.howone/ai/manifest.json` — copy the exact UUID |
|
|
527
523
|
| `howone.ai.run.generateStory(input)` | `howone.ai.generateStory.run(input)` |
|
|
528
524
|
| Action named `run`, `stream`, or `events` | Rename to e.g. `executeWorkflow`, `streamContent` |
|
|
529
|
-
|
|
|
530
|
-
|
|
|
525
|
+
| Passing raw JSON Schema from manifest into `defineAiAction` | Convert JSON Schema fields to Zod first |
|
|
526
|
+
| Making every output field `.optional()` or adding `.passthrough()` after validation fails | Keep manifest-required output fields required; inspect `AiSchemaValidationError.issues` and fix the contract/workflow mismatch |
|
|
527
|
+
| Reading `raw.finalResult`, `raw.data.result`, or `raw.result` after a typed `.run()` | Use the returned value directly when `outputSchema` is configured |
|
|
531
528
|
| Calling `howone.ai.generateStory.run(input)` inside JSX render | Move to event handler or useEffect |
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# AI Capability Architecture
|
|
2
|
+
|
|
3
|
+
Use this reference when a HowOne app needs AI generation, editing, analysis, research, or other
|
|
4
|
+
workflow-backed behavior.
|
|
5
|
+
|
|
6
|
+
## Core Split
|
|
7
|
+
|
|
8
|
+
HowOne AI has four separate layers:
|
|
9
|
+
|
|
10
|
+
1. **Capability contract**: `ai-capability-design` owns the name, description, input schema, output
|
|
11
|
+
schema, output entity name, workflow ID, versions, preview, apply, restore, and manifest sync.
|
|
12
|
+
2. **External workflow implementation**: `external-ai-capability` submits create/update operations
|
|
13
|
+
to the workflow service using the synced contract from `.howone/ai/manifest.json`.
|
|
14
|
+
3. **Workflow status/background layer**: status checking, request tracking, completion, failure, and
|
|
15
|
+
durable background-task behavior are handled outside the contract tool. Preserve request IDs and
|
|
16
|
+
workflow config IDs for that layer.
|
|
17
|
+
4. **SDK binding and UI**: `src/lib/sdk.ts` is generated from `.howone/ai/manifest.json`, then the
|
|
18
|
+
app calls `howone.ai.<capability>.run()`, `.stream()`, or `.events()`.
|
|
19
|
+
|
|
20
|
+
Do not collapse these layers. The common failure is putting workflow implementation, persistence,
|
|
21
|
+
and UI details into the capability schema.
|
|
22
|
+
|
|
23
|
+
## Source of Truth
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
agent proposal != source of truth
|
|
27
|
+
applied AI capability version = source of truth
|
|
28
|
+
.howone/ai/manifest.json = local source for codegen
|
|
29
|
+
src/lib/sdk.ts = generated binding output
|
|
30
|
+
external workflow service = implementation builder/editor
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The agent proposes a contract. The backend validates and versions it. The sync tool writes the
|
|
34
|
+
manifest. App code is generated from the manifest.
|
|
35
|
+
|
|
36
|
+
## Standard AI Feature Workflow
|
|
37
|
+
|
|
38
|
+
1. Inspect current AI state with `ai-capability-design`.
|
|
39
|
+
2. Design one complete capability patch for the feature.
|
|
40
|
+
3. Preview the patch.
|
|
41
|
+
4. Apply the same operations.
|
|
42
|
+
5. Sync AI artifacts.
|
|
43
|
+
6. Read `.howone/ai/manifest.json`.
|
|
44
|
+
7. Submit external workflow create/update with `external-ai-capability`.
|
|
45
|
+
8. Preserve returned `requestIds` for the status/background-task layer.
|
|
46
|
+
9. Generate or update `src/lib/sdk.ts` from the manifest.
|
|
47
|
+
10. Implement UI calls through `howone.ai.*`.
|
|
48
|
+
11. If generated output must be stored, design database entities after the AI output schema exists.
|
|
49
|
+
|
|
50
|
+
Do not preview a patch and then submit different operations. Do not restate schemas in
|
|
51
|
+
`external-ai-capability`; it loads the manifest.
|
|
52
|
+
|
|
53
|
+
## Unsupported AI Requests
|
|
54
|
+
|
|
55
|
+
If the user explicitly asks for AI capability and the feature cannot be implemented with the
|
|
56
|
+
available workflow service capabilities, stop the AI implementation task and report the gap.
|
|
57
|
+
|
|
58
|
+
Do not:
|
|
59
|
+
|
|
60
|
+
- fake the AI feature with static mock data.
|
|
61
|
+
- implement a frontend-only placeholder that pretends AI works.
|
|
62
|
+
- design a workflow that assumes unavailable tools, providers, APIs, or private datasets.
|
|
63
|
+
- silently drop the AI part and continue building a non-AI version.
|
|
64
|
+
- move forbidden work such as database CRUD, auth, upload handling, payments, or app state into the
|
|
65
|
+
workflow.
|
|
66
|
+
|
|
67
|
+
The correct response is to state which requested AI behavior is unsupported, which capability is
|
|
68
|
+
missing or which rule blocks it, and what narrower supported alternative could be built.
|
|
69
|
+
|
|
70
|
+
Only continue implementation when the remaining feature still satisfies the user's intent after
|
|
71
|
+
the unsupported AI capability is removed or narrowed.
|
|
72
|
+
|
|
73
|
+
## Create vs Update
|
|
74
|
+
|
|
75
|
+
Use `external-ai-capability` with `mode: "create"` when a capability has no external implementation.
|
|
76
|
+
The tool reads `workflowId`, `inputSchema`, `outputSchema`, and `outputEntityName` from the synced
|
|
77
|
+
manifest.
|
|
78
|
+
|
|
79
|
+
Use `mode: "update"` when the external workflow implementation needs to change. Updates require:
|
|
80
|
+
|
|
81
|
+
- `capabilityName`
|
|
82
|
+
- `workflowConfigID`
|
|
83
|
+
- `updatePrompt`
|
|
84
|
+
|
|
85
|
+
`workflowConfigID` must come from a confirmed workflow status result, specifically
|
|
86
|
+
`payload.workflow_details.new_workflow_config_id`. Do not invent it, omit it, or substitute
|
|
87
|
+
`workflowId`.
|
|
88
|
+
|
|
89
|
+
If the input/output schema changes, update the capability contract first, sync the manifest, then
|
|
90
|
+
submit the external workflow update.
|
|
91
|
+
|
|
92
|
+
## Persistence Boundary
|
|
93
|
+
|
|
94
|
+
AI workflows produce outputs. They do not persist app state.
|
|
95
|
+
|
|
96
|
+
Workflow can do:
|
|
97
|
+
|
|
98
|
+
- text generation, summarization, translation, classification, extraction
|
|
99
|
+
- image, video, and audio generation/editing/analysis
|
|
100
|
+
- web research and page crawling
|
|
101
|
+
- financial or academic retrieval
|
|
102
|
+
- file generation or file reading through URLs
|
|
103
|
+
|
|
104
|
+
Workflow must not do:
|
|
105
|
+
|
|
106
|
+
- database create/read/update/delete
|
|
107
|
+
- authentication or session handling
|
|
108
|
+
- upload handling
|
|
109
|
+
- payment processing
|
|
110
|
+
- app state management
|
|
111
|
+
- owner assignment or record permissions
|
|
112
|
+
|
|
113
|
+
If the app saves AI output, derive entity fields from `outputSchema` and add only necessary request
|
|
114
|
+
metadata such as prompt, selected options, status, timestamps, or references.
|
|
115
|
+
|
|
116
|
+
## Workflow Count
|
|
117
|
+
|
|
118
|
+
Default rule: one user-facing AI feature maps to one capability and one workflow.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
|
|
122
|
+
- Story + illustration generator: one workflow that returns story text and generated image URLs.
|
|
123
|
+
- Image edit feature: one workflow that accepts source image URL and edit instruction, returns
|
|
124
|
+
edited image URL.
|
|
125
|
+
- News briefing: one workflow that searches and summarizes, returns briefing and source links.
|
|
126
|
+
|
|
127
|
+
RAG is the exception:
|
|
128
|
+
|
|
129
|
+
- indexing workflow: ingests documents and creates retrieval/indexing artifacts.
|
|
130
|
+
- query workflow: answers questions from the indexed knowledge base.
|
|
131
|
+
|
|
132
|
+
Do not split normal sequential AI steps into multiple capabilities unless they are independently
|
|
133
|
+
triggered product features.
|
|
134
|
+
|
|
135
|
+
## Capability Naming
|
|
136
|
+
|
|
137
|
+
Use stable JavaScript-safe identifiers for capability names, for example `generateIllustration` or
|
|
138
|
+
`summarizeDocument`. Do not use display labels as identifiers.
|
|
139
|
+
|
|
140
|
+
Avoid names that collide with action methods: `run`, `stream`, `events`.
|
|
141
|
+
|
|
142
|
+
Use `outputEntityName` only when the generated output has a natural persisted record shape.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Workflow Contract Rules
|
|
2
|
+
|
|
3
|
+
Use this reference when designing `inputSchema`, `outputSchema`, and capability descriptions for
|
|
4
|
+
HowOne AI workflows.
|
|
5
|
+
|
|
6
|
+
## Loose JSON Schema
|
|
7
|
+
|
|
8
|
+
Workflow schemas should be loose enough for an AI workflow engine.
|
|
9
|
+
|
|
10
|
+
Rules:
|
|
11
|
+
|
|
12
|
+
- Require only essential inputs.
|
|
13
|
+
- Mark non-essential options optional.
|
|
14
|
+
- Prefer `string` with a clear description over restrictive enums when user values are open-ended.
|
|
15
|
+
- Avoid `minLength`, `maxLength`, `pattern`, and narrow validation unless there is a concrete
|
|
16
|
+
technical reason.
|
|
17
|
+
- Avoid nested objects when a simple described string is enough.
|
|
18
|
+
- Avoid exposing implementation knobs the user does not need.
|
|
19
|
+
|
|
20
|
+
Good:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"tone": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Desired writing tone, e.g. formal, casual, humorous, empathetic, professional."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use enums only for truly closed domains.
|
|
32
|
+
|
|
33
|
+
## URLs, Not Raw Files
|
|
34
|
+
|
|
35
|
+
All file exchange uses URLs.
|
|
36
|
+
|
|
37
|
+
Rules:
|
|
38
|
+
|
|
39
|
+
- File inputs are strings with `format: "uri"`, such as `document_url`, `source_image_url`, or
|
|
40
|
+
`audio_file_url`.
|
|
41
|
+
- File outputs are strings with `format: "uri"`, such as `generated_image_url`, `edited_video_url`,
|
|
42
|
+
or `result_pdf_url`.
|
|
43
|
+
- Never use base64, raw bytes, inline file content, or `contentEncoding`.
|
|
44
|
+
- The app uploads user files first, then passes the URL to the workflow.
|
|
45
|
+
|
|
46
|
+
## Output Minimalism
|
|
47
|
+
|
|
48
|
+
The workflow will try to fill every output field. Only ask for fields the product needs.
|
|
49
|
+
|
|
50
|
+
Usually forbidden unless explicitly requested:
|
|
51
|
+
|
|
52
|
+
- timestamps and processing time
|
|
53
|
+
- file size, MIME type, encoding, dimensions, frame rate, bitrate
|
|
54
|
+
- confidence scores and bounding boxes
|
|
55
|
+
- model, provider, version, cost, or internal execution metadata
|
|
56
|
+
- style/tone metadata when the user only asked for an asset
|
|
57
|
+
|
|
58
|
+
Examples:
|
|
59
|
+
|
|
60
|
+
| User Need | Good Output | Avoid |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Summarize a document | `summary` | `summary`, `word_count`, `reading_time` |
|
|
63
|
+
| Generate an image | `generated_image_url` | `generated_image_url`, `model_used`, `color_palette` |
|
|
64
|
+
| OCR an image | `extracted_text` | `extracted_text`, `confidence_score`, `bounding_boxes` |
|
|
65
|
+
|
|
66
|
+
## No Input/Output Name Overlap
|
|
67
|
+
|
|
68
|
+
Input and output property names must not overlap. The workflow engine uses a shared parameter
|
|
69
|
+
namespace.
|
|
70
|
+
|
|
71
|
+
Bad:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"inputSchema": { "properties": { "text": { "type": "string" } } },
|
|
76
|
+
"outputSchema": { "properties": { "text": { "type": "string" } } }
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Good:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"inputSchema": { "properties": { "source_text": { "type": "string" } } },
|
|
85
|
+
"outputSchema": { "properties": { "translated_text": { "type": "string" } } }
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Common pairs:
|
|
90
|
+
|
|
91
|
+
- `source_text` -> `translated_text`
|
|
92
|
+
- `source_content` -> `summary`
|
|
93
|
+
- `source_image_url` -> `edited_image_url`
|
|
94
|
+
- `description_prompt` -> `generated_image_url`
|
|
95
|
+
|
|
96
|
+
## Output Language
|
|
97
|
+
|
|
98
|
+
Every text output field description must specify the language rule.
|
|
99
|
+
|
|
100
|
+
Use one of these patterns:
|
|
101
|
+
|
|
102
|
+
- fixed: "Summary in English."
|
|
103
|
+
- input-driven: "Translated text in the target language specified by `target_language`."
|
|
104
|
+
- source-driven: "Summary in the same language as the source document."
|
|
105
|
+
- mixed: "Extracted text, which may contain mixed Chinese and English content."
|
|
106
|
+
|
|
107
|
+
Do not write vague descriptions like "The translated text."
|
|
108
|
+
|
|
109
|
+
## Description Says What, Not How
|
|
110
|
+
|
|
111
|
+
`capability.description` describes the user outcome.
|
|
112
|
+
|
|
113
|
+
Do:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"description": "Searches the web for the latest news on a topic and produces a structured briefing with source links."
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Do not:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"description": "First calls search_web, then summarizes each article with an LLM, then saves JSON."
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Never mention internal tool names, step sequences, providers, or model names in the capability
|
|
130
|
+
description.
|
|
131
|
+
|
|
132
|
+
## Available Capability Families
|
|
133
|
+
|
|
134
|
+
Design only around capabilities available to the workflow service:
|
|
135
|
+
|
|
136
|
+
- web search and page crawling
|
|
137
|
+
- image generation, editing, analysis, OCR
|
|
138
|
+
- video generation, editing, concatenation, frame extraction
|
|
139
|
+
- audio generation, recognition, merging
|
|
140
|
+
- financial price history retrieval
|
|
141
|
+
- academic paper search and bibliography
|
|
142
|
+
- file storage/read for JSON, YAML, CSV, PDF, Markdown, TXT
|
|
143
|
+
- email sending
|
|
144
|
+
- RSS feed fetching
|
|
145
|
+
- Airbnb search
|
|
146
|
+
- Hacker News search
|
|
147
|
+
|
|
148
|
+
If the requested product needs unavailable functionality, redesign the feature around available
|
|
149
|
+
capabilities or exclude it explicitly.
|
|
150
|
+
|
|
151
|
+
When the user explicitly requires the unavailable AI behavior, terminate the AI task instead of
|
|
152
|
+
building a fake or silently degraded implementation. Report the unsupported requirement and the
|
|
153
|
+
closest supported alternative.
|
|
154
|
+
|
|
155
|
+
## External Data Assumptions
|
|
156
|
+
|
|
157
|
+
Do not assume the user can provide external datasets unless they explicitly say so.
|
|
158
|
+
|
|
159
|
+
For "stock analysis", use a workflow input such as `trading_symbol` and let the workflow retrieve
|
|
160
|
+
history. Do not require `stock_data_csv_url` unless the user says they have a CSV.
|
|
161
|
+
|
|
162
|
+
For "latest news", use web search inside the workflow. Do not ask the user to provide article URLs
|
|
163
|
+
unless that is the product requirement.
|
|
164
|
+
|
|
165
|
+
## MVP Limit
|
|
166
|
+
|
|
167
|
+
Keep AI app generation to at most five core features. Exclude feedback forms, onboarding tutorials,
|
|
168
|
+
notification settings, export, sharing, personalization, and preferences unless the user explicitly
|
|
169
|
+
requests them.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# AI SDK Handoff
|
|
2
|
+
|
|
3
|
+
Use this reference after AI capability artifacts have been synced and app code needs to call the
|
|
4
|
+
workflow.
|
|
5
|
+
|
|
6
|
+
## Binding Source
|
|
7
|
+
|
|
8
|
+
Generate `src/lib/sdk.ts` from `.howone/ai/manifest.json`. Do not write AI bindings from memory or
|
|
9
|
+
from the original user prompt.
|
|
10
|
+
|
|
11
|
+
For each manifest capability:
|
|
12
|
+
|
|
13
|
+
1. Read `name`.
|
|
14
|
+
2. Read `workflowId`.
|
|
15
|
+
3. Read `inputSchema`.
|
|
16
|
+
4. Read `outputSchema`.
|
|
17
|
+
5. Generate zod input and output schemas from the JSON Schema fields.
|
|
18
|
+
6. Bind with `defineAiAction(name, { workflowId, inputSchema, outputSchema })`.
|
|
19
|
+
|
|
20
|
+
`workflowId` is mandatory. Without it, the SDK falls back to the action name as the execution URL
|
|
21
|
+
segment, which is not a workflow UUID.
|
|
22
|
+
|
|
23
|
+
## Output Handling
|
|
24
|
+
|
|
25
|
+
For a typed action with `outputSchema`, `.run()` returns the validated workflow output payload.
|
|
26
|
+
The SDK unwraps the EAX execution envelope and validates `finalResult` internally.
|
|
27
|
+
|
|
28
|
+
Use this pattern:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const output = await howone.ai.generateSummary.run(input)
|
|
32
|
+
// output is GenerateSummaryOutput when outputSchema is configured.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Do not read `raw.finalResult`, `raw.result`, or `raw.data.result` from a typed action result. Those
|
|
36
|
+
paths are execution/SSE internals. App code should use the value returned by `.run()` directly.
|
|
37
|
+
|
|
38
|
+
Do not make every output field `.optional()` or add `.passthrough()` to hide validation errors.
|
|
39
|
+
Required fields in the manifest must stay required in Zod. If validation fails, inspect
|
|
40
|
+
`AiSchemaValidationError.issues` and fix the capability contract or workflow output mapping.
|
|
41
|
+
|
|
42
|
+
## UI State
|
|
43
|
+
|
|
44
|
+
AI calls should run in event handlers, effects, or explicit async actions. Never call
|
|
45
|
+
`howone.ai.*.run()` inside JSX render.
|
|
46
|
+
|
|
47
|
+
Recommended UI states:
|
|
48
|
+
|
|
49
|
+
- idle
|
|
50
|
+
- running
|
|
51
|
+
- succeeded
|
|
52
|
+
- failed
|
|
53
|
+
- cancelled when using streaming
|
|
54
|
+
|
|
55
|
+
For streaming, keep the `AiSession` in a ref and call `cancel()` from the UI.
|
|
56
|
+
|
|
57
|
+
## Persistence Handoff
|
|
58
|
+
|
|
59
|
+
If the app stores generated output:
|
|
60
|
+
|
|
61
|
+
1. Run the AI workflow.
|
|
62
|
+
2. Use the typed output returned by `.run()`.
|
|
63
|
+
3. Write the resulting data through `howone.entities.*`.
|
|
64
|
+
|
|
65
|
+
Do not ask the workflow to write to the database. Do not pass owner fields for authenticated
|
|
66
|
+
user-owned entities; HowOne derives ownership from the JWT.
|
|
67
|
+
|
|
68
|
+
## Workflow Edit Handoff
|
|
69
|
+
|
|
70
|
+
When editing an external workflow implementation later, use `external-ai-capability` with:
|
|
71
|
+
|
|
72
|
+
- `mode: "update"`
|
|
73
|
+
- `capabilityName`
|
|
74
|
+
- `workflowConfigID`
|
|
75
|
+
- `updatePrompt`
|
|
76
|
+
|
|
77
|
+
`workflowConfigID` is not `workflowId`. It comes from a confirmed workflow status result:
|
|
78
|
+
`payload.workflow_details.new_workflow_config_id`.
|
|
79
|
+
|
|
80
|
+
If the schema changed, update and sync the capability contract before submitting the workflow edit.
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: howone-sdk
|
|
3
|
-
description:
|
|
3
|
+
description: Required operating manual for HowOne generated apps. Use for backend schema/API design, frontend UI implementation that reads or writes HowOne data, AI capability/workflow design, SDK binding/codegen from .howone manifests, auth, uploads, public/private access, and any code that calls @howone/sdk, howone.entities.*, howone.public.entities.*, or howone.ai.*.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# HowOne App Architecture Skill
|
|
7
7
|
|
|
8
|
-
This skill is
|
|
9
|
-
|
|
8
|
+
This skill is the operating manual for generated HowOne apps. Use it before making platform
|
|
9
|
+
decisions for backend data, frontend data access, auth, AI capabilities, SDK bindings, or generated
|
|
10
|
+
app code that depends on HowOne runtime behavior.
|
|
11
|
+
|
|
12
|
+
Load the smallest set of numbered references that match the task, but do not skip this skill for
|
|
13
|
+
backend schema design, frontend implementation, or AI features. The references define the contracts
|
|
14
|
+
that app code must follow.
|
|
10
15
|
|
|
11
16
|
## Active Tracks
|
|
12
17
|
|
|
@@ -15,19 +20,22 @@ that matches the task; do not read every file by default.
|
|
|
15
20
|
| `01-architect/` | App generation + manifest flow | End-to-end HowOne app generation flow: when to design backend, when to sync manifests, when to update SDK bindings, and how to choose auth/data posture. |
|
|
16
21
|
| `02-database/` | Backend schema + data access design | Entity schema contract, schema operations, access modes, owner/public data posture, indexes, versions, and guardrails. |
|
|
17
22
|
| `03-sdk/` | Frontend SDK usage | `src/lib/sdk.ts`, auth, React provider, entity calls, public data, uploads, raw HTTP, AI action calls. |
|
|
18
|
-
|
|
19
|
-
Reserved directory:
|
|
20
|
-
|
|
21
|
-
- `04-ai/` is intentionally present for future AI capability design. Do not use it yet. For now,
|
|
22
|
-
only use `03-sdk/07-ai-action-calls.md` when wiring an already-synced AI action into app code.
|
|
23
|
+
| `04-ai/` | AI capability + workflow design | HowOne AI capability contracts, external workflow create/update submission, workflow schema rules, persistence boundaries, and SDK handoff. |
|
|
23
24
|
|
|
24
25
|
## Routing
|
|
25
26
|
|
|
26
|
-
Read references by task shape
|
|
27
|
+
Read references by task shape. Prefer exact references over generic examples.
|
|
27
28
|
|
|
28
29
|
- New HowOne app or broad feature planning: read `01-architect/01-app-generation.md`.
|
|
30
|
+
- Any feature touching backend data, frontend data access, or saved records: read the architect
|
|
31
|
+
flow first, then the relevant database and SDK references below.
|
|
32
|
+
- Any feature touching AI generation, AI workflow behavior, or AI outputs: read the AI references
|
|
33
|
+
first, then the SDK handoff/action-call references.
|
|
34
|
+
- AI capability, AI workflow generation/editing, or full-stack AI feature planning: read
|
|
35
|
+
`04-ai/01-ai-capability-architecture.md` and `04-ai/02-workflow-contract-rules.md`.
|
|
29
36
|
- Backend database/schema creation or change: read `02-database/01-schema-design.md` and
|
|
30
37
|
`02-database/02-schema-operations.md`.
|
|
38
|
+
- AI output persistence: read the `04-ai/` files first, then `02-database/01-schema-design.md`.
|
|
31
39
|
- After schema sync, when app code must call the entity: read
|
|
32
40
|
`01-architect/02-manifest-codegen.md` and `03-sdk/02-entity-operations.md`.
|
|
33
41
|
- Custom login page, Email OTP, Phone OTP, Google, GitHub, token persistence, repeated login,
|
|
@@ -38,6 +46,28 @@ Read references by task shape:
|
|
|
38
46
|
`03-sdk/02-entity-operations.md`, and `01-architect/02-manifest-codegen.md`.
|
|
39
47
|
- File upload: read `03-sdk/05-file-upload.md`.
|
|
40
48
|
- Raw HTTP escape hatch: read `03-sdk/06-raw-http.md`.
|
|
49
|
+
- App-side AI action calls after `.howone/ai/manifest.json` exists: read
|
|
50
|
+
`03-sdk/07-ai-action-calls.md`.
|
|
51
|
+
|
|
52
|
+
## Reference Selection Protocol
|
|
53
|
+
|
|
54
|
+
Before writing code, classify the touched surfaces:
|
|
55
|
+
|
|
56
|
+
| Touched surface | Required references |
|
|
57
|
+
|---|---|
|
|
58
|
+
| New app, feature architecture, or uncertain data posture | `01-architect/01-app-generation.md` |
|
|
59
|
+
| Entity/schema/access/index change | `02-database/01-schema-design.md`, `02-database/02-schema-operations.md` |
|
|
60
|
+
| Existing synced manifest to TypeScript bindings | `01-architect/02-manifest-codegen.md` |
|
|
61
|
+
| UI reads/writes entities | `03-sdk/01-client-setup.md`, `03-sdk/02-entity-operations.md` |
|
|
62
|
+
| Public read/share flow | `02-database/03-data-access-patterns.md`, `03-sdk/02-entity-operations.md` |
|
|
63
|
+
| Auth/session/login behavior | `03-sdk/03-auth.md`, `03-sdk/04-react-integration.md` |
|
|
64
|
+
| AI capability or workflow design | `04-ai/01-ai-capability-architecture.md`, `04-ai/02-workflow-contract-rules.md` |
|
|
65
|
+
| AI manifest handoff to app code | `04-ai/03-ai-sdk-handoff.md`, `03-sdk/07-ai-action-calls.md` |
|
|
66
|
+
| File upload | `03-sdk/05-file-upload.md` |
|
|
67
|
+
| Raw HTTP escape hatch | `03-sdk/06-raw-http.md` |
|
|
68
|
+
|
|
69
|
+
If a task spans multiple surfaces, read one reference from each surface before editing. Do not
|
|
70
|
+
invent API parameters, response shapes, owner fields, workflow IDs, or output paths from memory.
|
|
41
71
|
|
|
42
72
|
## Core Workflow
|
|
43
73
|
|
|
@@ -52,6 +82,20 @@ For app features that touch backend data:
|
|
|
52
82
|
6. Implement frontend calls using the SDK track.
|
|
53
83
|
7. Validate with the app's typecheck/build.
|
|
54
84
|
|
|
85
|
+
For app features that touch AI:
|
|
86
|
+
|
|
87
|
+
1. Decide the AI feature boundary in the AI track: one workflow per feature, except RAG uses
|
|
88
|
+
indexing and query workflows.
|
|
89
|
+
2. Design or update the AI capability contract with `ai-capability-design`.
|
|
90
|
+
3. Preview/apply the same AI patch, then sync AI artifacts.
|
|
91
|
+
4. Submit external workflow create/update with `external-ai-capability`.
|
|
92
|
+
5. Preserve returned request IDs for the workflow status/background-task layer.
|
|
93
|
+
6. Read `.howone/ai/manifest.json`.
|
|
94
|
+
7. Generate or update app SDK bindings from the manifest.
|
|
95
|
+
8. Implement frontend calls using `howone.ai.*`.
|
|
96
|
+
9. If outputs are saved, derive entity fields from the AI `outputSchema`, then follow the database
|
|
97
|
+
workflow.
|
|
98
|
+
|
|
55
99
|
For frontend-only SDK work:
|
|
56
100
|
|
|
57
101
|
1. Read existing `src/lib/sdk.ts`.
|
|
@@ -73,3 +117,10 @@ For frontend-only SDK work:
|
|
|
73
117
|
reads or writes.
|
|
74
118
|
- For custom in-app login, use `HowOneProvider auth="none"`, call
|
|
75
119
|
`howone.auth.setToken(token)` on success, and verify startup session with `await howone.me()`.
|
|
120
|
+
- AI workflows are implementation, not persistence. Never put CRUD, auth, upload handling, or app
|
|
121
|
+
state management into the workflow capability contract.
|
|
122
|
+
- For external workflow edits, pass `workflowConfigID` from a confirmed workflow status result; do
|
|
123
|
+
not invent it or omit it.
|
|
124
|
+
- If the user explicitly requires AI behavior that the available workflow service cannot support,
|
|
125
|
+
stop that AI implementation path and report the unsupported requirement instead of faking,
|
|
126
|
+
silently omitting, or replacing the AI capability with static behavior.
|
|
@@ -18,6 +18,9 @@ export const entities = defineEntities({
|
|
|
18
18
|
|
|
19
19
|
export const ai = defineAiActions({
|
|
20
20
|
// Add generated AI action bindings here, for example:
|
|
21
|
+
// import { z } from "zod" and define Zod schemas before using defineAiAction.
|
|
22
|
+
// Do not paste JSON Schema objects from .howone/ai/manifest.json here directly.
|
|
23
|
+
// With outputSchema configured, howone.ai.<action>.run() returns the validated finalResult payload.
|
|
21
24
|
// generateImage: defineAiAction("generateImage", {
|
|
22
25
|
// workflowId: "<workflow-uuid>", // workflow ID for this capability
|
|
23
26
|
// inputSchema: generateImageInputSchema,
|