howone 0.2.2 → 0.2.5

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 (30) hide show
  1. package/package.json +1 -1
  2. package/templates/vite/.howone/skills/howone/01-architect/01-app-generation.md +36 -7
  3. package/templates/vite/.howone/skills/howone/01-architect/02-manifest-codegen.md +121 -436
  4. package/templates/vite/.howone/skills/howone/03-ai-capabilities/03-service-capability-catalog.md +37 -19
  5. package/templates/vite/.howone/skills/howone/03-ai-capabilities/04-workflow-operations.md +14 -5
  6. package/templates/vite/.howone/skills/howone/04-app-sdk/01-client-setup.md +94 -261
  7. package/templates/vite/.howone/skills/howone/04-app-sdk/02-entity-operations.md +85 -465
  8. package/templates/vite/.howone/skills/howone/04-app-sdk/03-auth.md +11 -7
  9. package/templates/vite/.howone/skills/howone/04-app-sdk/04-react-integration.md +84 -137
  10. package/templates/vite/.howone/skills/howone/04-app-sdk/05-file-upload.md +66 -273
  11. package/templates/vite/.howone/skills/howone/04-app-sdk/06-raw-http.md +72 -249
  12. package/templates/vite/.howone/skills/howone/04-app-sdk/07-ai-action-calls.md +135 -499
  13. package/templates/vite/.howone/skills/howone/04-app-sdk/08-ai-manifest-handoff.md +49 -196
  14. package/templates/vite/.howone/skills/howone/04-app-sdk/09-extension-boundaries.md +4 -4
  15. package/templates/vite/.howone/skills/howone/04-app-sdk/10-workflow-execute-sse.md +94 -61
  16. package/templates/vite/.howone/skills/howone/04-app-sdk/11-entity-data-access-patterns.md +4 -3
  17. package/templates/vite/.howone/skills/howone/SKILL.md +110 -4
  18. package/templates/vite/.howone/skills/howone/references/audio-generation.md +30 -0
  19. package/templates/vite/.howone/skills/howone/references/audio-recognition.md +30 -0
  20. package/templates/vite/.howone/skills/howone/references/common-errors.md +27 -0
  21. package/templates/vite/.howone/skills/howone/references/finance.md +28 -0
  22. package/templates/vite/.howone/skills/howone/references/image-editing.md +30 -0
  23. package/templates/vite/.howone/skills/howone/references/image-generation.md +30 -0
  24. package/templates/vite/.howone/skills/howone/references/version-evidence.md +47 -0
  25. package/templates/vite/.howone/skills/howone/references/video-generation.md +35 -0
  26. package/templates/vite/.howone/skills/howone/scripts/verify-project.mjs +151 -0
  27. package/templates/vite/package.json +1 -1
  28. package/templates/vite/src/App.tsx +9 -5
  29. package/templates/vite/src/lib/sdk.ts +7 -5
  30. package/templates/vite/bun.lock +0 -1478
@@ -1,41 +1,27 @@
1
- # AI Manifest Handoff (App SDK)
1
+ # AI Manifest Handoff
2
2
 
3
- Use this reference after AI capability artifacts have been synced and app code must call the
4
- workflow through `@howone/sdk`.
3
+ Use this track after `sync_ai_artifacts` and external workflow submission. It is the short handoff
4
+ from `.howone/ai/manifest.json` to `src/lib/sdk.ts`; use `07-ai-action-calls.md` for execution
5
+ semantics and `10-workflow-execute-sse.md` for wire details.
5
6
 
6
- This file answers: **how does `.howone/ai/manifest.json` become `src/lib/sdk.ts`, and how should UI
7
- call it?**
7
+ ## Handoff protocol
8
8
 
9
- For live stream wire details, read `04-app-sdk/10-workflow-execute-sse.md`. The current endpoint emits
10
- only `run_start`, `progress`, `run_complete`, `run_error`, and `credit_insufficient`.
9
+ 1. Read the current manifest, not a cached tool response.
10
+ 2. Confirm every action has an exact UUID `workflowId`.
11
+ 3. Generate Zod input/output schemas from the manifest.
12
+ 4. Bind with `defineAiAction` and compose with `withAiActions`.
13
+ 5. Run `node .howone/skills/howone/scripts/verify-project.mjs`, typecheck, and build.
11
14
 
12
- ## Binding Source
15
+ Workflow create/update can rotate the config UUID. After every successful external workflow
16
+ operation, sync and re-read the manifest before deciding whether the app binding changed.
13
17
 
14
- Generate `src/lib/sdk.ts` from `.howone/ai/manifest.json`. Do not write AI bindings from memory,
15
- from the original prompt, or from the workflow service response.
18
+ There is no action-name fallback. A missing UUID is a module initialization error, not a server
19
+ request that can be repaired later.
16
20
 
17
- For each manifest capability/action:
18
-
19
- 1. Read stable action name/ID.
20
- 2. Read `workflowId`.
21
- 3. Read `inputSchema`.
22
- 4. Read `outputSchema`.
23
- 5. Generate Zod input and output schemas.
24
- 6. Bind with `defineAiAction(name, { workflowId, inputSchema, outputSchema })`.
25
- 7. Compose with `withAiActions(client, ai)`.
26
-
27
- `workflowId` is mandatory. Without it, the SDK falls back to the action name as the execution URL
28
- segment, and the workflow service will reject it because the segment is not a UUID.
29
-
30
- ## Generated Binding Example
21
+ ## Example
31
22
 
32
23
  ```ts
33
- import {
34
- createClient,
35
- defineAiAction,
36
- defineAiActions,
37
- withAiActions,
38
- } from '@howone/sdk'
24
+ import { createClient, defineAiAction, defineAiActions, withAiActions } from '@howone/sdk'
39
25
  import { z } from 'zod'
40
26
 
41
27
  const client = createClient({
@@ -43,22 +29,14 @@ const client = createClient({
43
29
  env: import.meta.env.VITE_HOWONE_ENV,
44
30
  })
45
31
 
46
- export const summarizeDocumentInputSchema = z.object({
47
- document_url: z.string().url(),
48
- summary_length: z.string().optional(),
49
- })
50
- export type SummarizeDocumentInput = z.infer<typeof summarizeDocumentInputSchema>
32
+ const inputSchema = z.object({ documentUrl: z.string().url() })
33
+ const outputSchema = z.object({ summary: z.string() })
51
34
 
52
- export const summarizeDocumentOutputSchema = z.object({
53
- summary: z.string(),
54
- })
55
- export type SummarizeDocumentOutput = z.infer<typeof summarizeDocumentOutputSchema>
56
-
57
- export const ai = defineAiActions({
35
+ const ai = defineAiActions({
58
36
  summarizeDocument: defineAiAction('summarizeDocument', {
59
37
  workflowId: '550e8400-e29b-41d4-a716-446655440000',
60
- inputSchema: summarizeDocumentInputSchema,
61
- outputSchema: summarizeDocumentOutputSchema,
38
+ inputSchema,
39
+ outputSchema,
62
40
  }),
63
41
  })
64
42
 
@@ -66,172 +44,47 @@ const howone = withAiActions(client, ai)
66
44
  export default howone
67
45
  ```
68
46
 
69
- ## JSON Schema To Zod
47
+ The UUID above is illustrative only. Copy the real value from the current manifest.
48
+
49
+ ## Schema conversion rules
70
50
 
71
51
  | JSON Schema | Zod |
72
52
  |---|---|
73
- | `string` | `z.string()` |
74
- | `string` + `format: "uri"` | `z.string().url()` |
75
- | `number` | `z.number()` |
76
- | `integer` | `z.number().int()` |
77
- | `boolean` | `z.boolean()` |
78
- | `array` of strings | `z.array(z.string())` |
79
- | `array` of objects | `z.array(z.object({ ... }))` |
80
- | `object` | `z.object({ ... })` |
81
- | string enum | `z.enum([...])` |
82
- | field not in `required` | `.optional()` |
83
- | nullable | `.nullable()` |
84
-
85
- Rules:
86
-
87
- - Required manifest fields must stay required in Zod.
88
- - Do not add `.passthrough()` to hide execution envelope problems.
89
- - Do not make outputs optional to silence validation failures.
90
- - If the workflow returns a different shape, fix the workflow/capability contract.
91
-
92
- ## Calling Actions
93
-
94
- For typed one-shot actions:
95
-
96
- ```ts
97
- const output = await howone.ai.summarizeDocument.run({
98
- document_url,
99
- summary_length: 'short',
100
- })
101
-
102
- setSummary(output.summary)
103
- ```
104
-
105
- When `outputSchema` exists, `.run()` returns the validated `finalResult` payload directly.
106
-
107
- Do not read:
108
-
109
- ```ts
110
- result.finalResult.summary
111
- result.data.summary
112
- result.raw.finalResult
113
- ```
114
-
115
- Those are execution-envelope paths, not the typed SDK action contract.
116
-
117
- ## Streaming And Events
118
-
119
- Use `.stream()` when UI needs live output or cancellation:
120
-
121
- ```ts
122
- const session = howone.ai.generateStory.stream(input, {
123
- onMessageChunk: (line) => appendLog(line),
124
- onProgress: (percent, line) => {
125
- if (line?.startsWith('[DISPLAY]')) setStatus(line.replace('[DISPLAY]', '').trim())
126
- if (percent === 100) setProgress(100)
127
- },
128
- onCreditInsufficient: (event) => showCreditError(event.message),
129
- onRunError: (event) => showExecutionError(event.message),
130
- onError: (error) => setError(error.message),
131
- onComplete: (result) => setRawResult(result),
132
- })
133
-
134
- cancelButton.onclick = () => session.cancel()
135
- const final = await session.result
136
- ```
137
-
138
- Use `.events()` when code wants an async iterable:
139
-
140
- ```ts
141
- for await (const event of howone.ai.generateStory.events(input)) {
142
- if (event.type === 'progress') {
143
- appendLog(String(event.message))
144
- }
145
- if (event.type === 'run_complete') {
146
- setFinalResult(event.message)
147
- }
148
- if (event.type === 'credit_insufficient') {
149
- showCreditError(String(event.message))
150
- }
151
- if (event.type === 'run_error') {
152
- showExecutionError(String(event.message))
153
- }
154
- }
155
- ```
156
-
157
- ## UI State
158
-
159
- The SDK returns data and exposes callbacks. The app owns all visible UI.
160
-
161
- Recommended states:
162
-
163
- ```ts
164
- type AiUiState<T> =
165
- | { status: 'idle' }
166
- | { status: 'running'; progress?: number }
167
- | { status: 'succeeded'; output: T }
168
- | { status: 'failed'; message: string }
169
- | { status: 'cancelled' }
170
- ```
53
+ | string | `z.string()` |
54
+ | string URI | `z.string().url()` |
55
+ | number/integer | `z.number()` / `z.number().int()` |
56
+ | boolean | `z.boolean()` |
57
+ | array | `z.array(item)` |
58
+ | object | `z.object({ ... })` |
59
+ | enum | `z.enum([...])` |
60
+ | nullable | `z.nullable(...)` |
61
+ | not required | `.optional()` |
171
62
 
172
- Do not add or import SDK toast APIs. Do not show SDK-owned overlays.
63
+ Do not make required fields optional and do not use `.passthrough()` to conceal a workflow result
64
+ shape mismatch. Typed `.run()` returns the validated final result directly.
173
65
 
174
- ## Persistence Handoff
175
-
176
- If AI output should survive refresh, use entity persistence after the action returns.
177
-
178
- For history-style products, prefer `runAiActionAndPersist()`:
66
+ ## Result handoff
179
67
 
180
68
  ```ts
181
- const result = await runAiActionAndPersist({
182
- entity: howone.entities.Generation,
183
- input: { prompt },
184
- createPending: (input) => ({
185
- prompt: input.prompt,
186
- status: 'pending',
187
- requestedAt: new Date().toISOString(),
188
- }),
189
- run: (input) => howone.ai.generateImage.run(input),
190
- mapCompleted: ({ output }) => ({
191
- status: 'completed',
192
- resultUrl: output.generated_image_url,
193
- completedAt: new Date().toISOString(),
194
- }),
195
- mapFailed: ({ error }) => ({
196
- status: 'failed',
197
- errorMessage: error instanceof Error ? error.message : 'Generation failed',
198
- }),
199
- })
200
- ```
201
-
202
- For simple save-after-success:
203
-
204
- ```ts
205
- const output = await howone.ai.summarizeDocument.run(input)
69
+ const output = await howone.ai.summarizeDocument.run({ documentUrl })
206
70
  await howone.entities.DocumentSummary.create({
207
- documentUrl: input.document_url,
71
+ documentUrl,
208
72
  summary: output.summary,
209
73
  status: 'completed',
210
74
  })
211
75
  ```
212
76
 
213
- Do not ask the workflow to write records. Do not pass owner fields for authenticated own entities.
214
-
215
- ## Workflow Edit Handoff
216
-
217
- When changing external workflow behavior later:
218
-
219
- 1. If schema changes, update AI capability contract first and sync manifest.
220
- 2. Submit update through `external-ai-capability` with `updates: [{ capabilityName, updatePrompt }]`.
221
- 3. Re-read `.howone/ai/manifest.json`; update may rotate `workflowId` to a fresh config UUID.
222
- 4. Regenerate SDK bindings whenever the manifest `workflowId`, input schema, or output schema changed.
223
- 5. Behavior-only updates still require checking the manifest before deciding SDK is unchanged.
77
+ For history, use pending-first `runAiActionAndPersist`. The workflow does not perform database CRUD;
78
+ the app persists through generated entity bindings.
224
79
 
225
- The SDK does not use old `workflowConfigID` status values. It binds to the current manifest
226
- `workflowId`, which is the EAX config id used in execution URLs.
80
+ For streaming, use `onMessageChunk`, await `session.result`, and catch terminal workflow errors.
81
+ For events, a terminal error event is yielded before the iterator rejects.
227
82
 
228
- ## Handoff Checklist
83
+ ## Final checklist
229
84
 
230
- - `.howone/ai/manifest.json` exists and is current.
231
- - Each action has `workflowId`.
232
- - Zod input/output schemas match manifest required fields.
233
- - `defineAiAction` uses action name + exact workflow UUID.
234
- - UI uses returned typed output, not raw execution envelope.
235
- - Streaming session is cancellable when UI exposes cancel.
236
- - Persistence goes through `howone.entities.*`.
237
- - Visible status/error UI is app-owned.
85
+ - [ ] Current AI manifest was read after the last sync.
86
+ - [ ] Action IDs and UUIDs match exactly.
87
+ - [ ] Zod requiredness/enums match the manifest.
88
+ - [ ] No `aiUrl`, `aiBaseUrl`, or raw EAX URL was added.
89
+ - [ ] App uses `HowOneProvider client={howone}`.
90
+ - [ ] Verifier, typecheck, and build pass.
@@ -86,7 +86,7 @@ Use callbacks instead:
86
86
 
87
87
  ```tsx
88
88
  <HowOneProvider
89
- auth="required"
89
+ client={howone}
90
90
  brand="visible"
91
91
  onAuthRedirect={({ mode, returnUrl }) => {
92
92
  setAuthUi({ redirecting: true, mode, returnUrl })
@@ -102,8 +102,8 @@ Use callbacks instead:
102
102
  Hide the logo only when explicitly requested:
103
103
 
104
104
  ```tsx
105
- <HowOneProvider brand="hidden" />
106
- <HowOneProvider showBrandButton={false} />
105
+ <HowOneProvider client={howone} brand="hidden" />
106
+ <HowOneProvider client={howone} showBrandButton={false} />
107
107
  ```
108
108
 
109
109
  ## UI Feedback Rule
@@ -132,7 +132,7 @@ For streaming workflows, use callbacks/events:
132
132
  const session = howone.ai.generateImage.stream(
133
133
  { prompt },
134
134
  {
135
- onStreamContent: (delta) => appendLog(delta),
135
+ onMessageChunk: (delta) => appendLog(delta),
136
136
  onProgress: (progress) => setProgress(progress),
137
137
  onError: (error) => setStatus({ type: 'error', message: error.message }),
138
138
  onComplete: (result) => setResult(result.finalResult),
@@ -1,52 +1,40 @@
1
1
  # Workflow Execute SSE
2
2
 
3
- Use this reference for `@howone/sdk` AI action streaming and raw workflow execution calls.
3
+ Use this reference when an app needs live workflow progress, cancellation, or raw event iteration.
4
+ For ordinary AI actions, start with `07-ai-action-calls.md` and use the typed action methods.
4
5
 
5
- ## Endpoint Contract
6
+ ## Environment and endpoint
6
7
 
7
- The SDK uses the current workflow execute SSE endpoint:
8
+ The SDK chooses the EAX base from `createClient({ env })`:
8
9
 
9
- | Method | Path |
10
+ | `env` | EAX base |
10
11
  |---|---|
11
- | `POST` | `/workflow/execute/{project_short_id}/{config_id}` |
12
+ | `local` | `https://eax-backend-orchestrator-dev.fly.dev` |
13
+ | `dev` | `https://eax-backend-orchestrator-dev.fly.dev` |
14
+ | `prod` | `https://eax-backend-orchestrator-prod.fly.dev` |
12
15
 
13
- The request body is:
16
+ Do not construct this URL in app code or override it with `aiUrl`. A workflow action calls:
14
17
 
15
- ```json
16
- {
17
- "inputs": {},
18
- "priority": "normal"
19
- }
18
+ ```text
19
+ POST {eaxBase}/workflow/execute/{projectId}/{workflowId}
20
20
  ```
21
21
 
22
- `priority` is optional. All requests require `Authorization: Bearer <JWT>`.
23
-
24
- The successful HTTP response includes:
25
-
26
- | Header | Meaning |
27
- |---|---|
28
- | `Content-Type: text/event-stream` | Streaming response |
29
- | `X-Run-Id` | Execution task id for status/cancel follow-up |
30
-
31
- Do not use or generate code for old endpoints such as
32
- `/workflow/{appId}/{workflowId}/execute_sse`.
22
+ The request body is `{ inputs: {}, priority?: 'low' | 'normal' | 'high' }` and carries the current
23
+ client token as `Authorization: Bearer ...`.
33
24
 
34
- ## Wire Format
25
+ ## Wire protocol
35
26
 
36
- Frames contain only a `data:` line and a blank line:
27
+ Frames are JSON `data:` lines separated by a blank line. The JSON envelope owns the ID and event
28
+ name; there are no separate legacy SSE `id:` or `event:` lines:
37
29
 
38
30
  ```text
39
- data: {"id":"evt_8ae6d6e8a4ea","event":"run_start","message":"execution with sse is started"}
31
+ data: {"id":"evt_1","event":"run_start","message":"execution with sse is started"}
40
32
 
41
- data: {"id":"evt_241ff985450b","event":"progress","message":"[DISPLAY] Executing node: Extract"}
33
+ data: {"id":"evt_2","event":"progress","message":"[DISPLAY] Executing node: Extract"}
42
34
 
43
- data: {"id":"evt_f3a1b2c3d4e5","event":"run_complete","message":{"video_url":"https://..."}}
35
+ data: {"id":"evt_3","event":"run_complete","message":{"imageUrl":"https://..."}}
44
36
  ```
45
37
 
46
- There are no separate SSE `event:` or `id:` lines. The JSON envelope owns those fields.
47
-
48
- ## Envelope
49
-
50
38
  ```ts
51
39
  type WorkflowSseEnvelope = {
52
40
  id: string
@@ -55,51 +43,96 @@ type WorkflowSseEnvelope = {
55
43
  }
56
44
  ```
57
45
 
58
- Event meanings:
59
-
60
- | Event | Message | Meaning |
61
- |---|---|---|
62
- | `run_start` | string | Stream opened and execution started. |
63
- | `progress` | string | One worker log line. `[DISPLAY]` lines are intended for UI. |
64
- | `run_complete` | object | Final workflow-specific output. SDK maps this to `AiResult.finalResult`. |
65
- | `run_error` | string | Workflow failed. |
66
- | `credit_insufficient` | string | Billing/credit block. |
67
-
68
46
  The terminal event is exactly one of `run_complete`, `run_error`, or `credit_insufficient`.
69
- There is no `stream_end` event.
70
-
71
- ## SDK Mapping
72
-
73
- `howone.ai.run(configId, inputs)` and typed action `.run()` call the endpoint above.
47
+ There is no `stream_end`, `node_start`, `tool_call_end`, or `ai_message_chunk` event in this
48
+ workflow endpoint.
74
49
 
75
- `AiResult` maps the stream as:
50
+ ## SDK mapping
76
51
 
77
52
  ```ts
78
53
  type AiResult = {
79
54
  success: boolean
80
- runId?: string
55
+ runId?: string // X-Run-Id response header
81
56
  outcome: 'success' | 'credit_insufficient' | 'run_error' | null
82
57
  finalResult: Record<string, unknown> | null
83
58
  progressLogs: string[]
59
+ stateData: Record<string, unknown> // deprecated; always empty for this protocol
60
+ nodeExecutions: unknown[] // deprecated; always empty
61
+ toolExecutions: unknown[] // deprecated; always empty
62
+ totalDuration: number
84
63
  errors: string[]
85
64
  events: AiEvent[]
86
65
  }
87
66
  ```
88
67
 
89
- For typed actions with an `outputSchema`, `.run()` returns the validated `run_complete.message`
90
- object directly.
68
+ `run_complete.message` becomes `finalResult`. `progress.message` lines append to `progressLogs`.
69
+ `run_error` and `credit_insufficient` append errors and cause `run()`/`stream.result` to reject with
70
+ `WorkflowExecutionError`; the `events()` iterator yields the terminal event before rejecting.
71
+
72
+ ## UI pattern
73
+
74
+ ```tsx
75
+ function WorkflowProgress({ input }: { input: GenerateInput }) {
76
+ const [logs, setLogs] = useState<string[]>([])
77
+ const [error, setError] = useState<string | null>(null)
78
+
79
+ async function run() {
80
+ const controller = new AbortController()
81
+ try {
82
+ const result = await howone.ai.generate.run(input, {
83
+ signal: controller.signal,
84
+ onMessageChunk: (line) => setLogs((current) => [...current, line]),
85
+ })
86
+ console.log(result)
87
+ } catch (value) {
88
+ setError(value instanceof Error ? value.message : String(value))
89
+ }
90
+ }
91
+
92
+ return <button onClick={() => void run()}>Run workflow</button>
93
+ }
94
+ ```
95
+
96
+ For a cancel button, keep the controller/session in a ref and call `abort()` or `session.cancel()`.
97
+ Treat abort as a user action in UI; do not report it as an EAX `run_error`.
91
98
 
92
- ## Callback Rules
99
+ ## Callback rules
93
100
 
94
- - `onRunStart(event)` receives the `run_start` envelope.
95
- - `onMessageChunk(text, event)` receives each `progress.message` line.
96
- - `onProgress(0, message)` receives each progress line; `onProgress(100)` fires on success.
97
- - `onRunComplete(event, result)` receives `event.message` as the final object.
98
- - `onRunError(event)` receives `event.message` as the error string.
99
- - `onCreditInsufficient(event)` receives `event.message` as the credit error string.
100
- - `onEvent(event, 'workflow')` fires for every parsed envelope.
101
+ - `onEvent(event, 'workflow')` fires for each parsed envelope.
102
+ - `onRunStart` fires for `run_start`.
103
+ - `onMessageChunk(text, event)` fires for each non-empty progress line.
104
+ - `onProgress(0, message)` is a compatibility callback for progress lines; `onProgress(100)` fires
105
+ on successful completion.
106
+ - `onRunComplete` receives the final object.
107
+ - `onRunError` and `onCreditInsufficient` receive terminal messages.
108
+ - `onComplete` receives the assembled result before a terminal failure is rejected.
109
+ - `onError` receives transport and terminal errors.
101
110
 
102
- Do not write UI code that expects `event.payload.result`, `event.payload.details.reason`,
103
- `node_start`, `tool_call_end`, `state_update`, or `ai_message_chunk`. Those belong to an old
104
- multi-channel protocol and are not part of the current workflow execute SSE contract.
111
+ Callbacks are not a substitute for awaiting `run()` or `session.result`; always handle the promise.
112
+ The SDK retains at most 1,000 events and 500 progress lines by default while still invoking callbacks
113
+ for every event. Configure the bounds for a diagnostic screen rather than retaining unbounded logs.
105
114
 
115
+ ## Raw event iteration
116
+
117
+ ```ts
118
+ try {
119
+ for await (const event of howone.ai.generate.events(input)) {
120
+ renderEvent(event)
121
+ }
122
+ } catch (error) {
123
+ renderFailure(error)
124
+ }
125
+ ```
126
+
127
+ On a terminal failure, consumers see the `run_error` or `credit_insufficient` event before the
128
+ iterator rejects. Breaking the loop cancels the underlying reader.
129
+
130
+ ## Common mistakes
131
+
132
+ | Mistake | Fix |
133
+ |---|---|
134
+ | Calling the old `/workflow/{appId}/{id}/execute_sse` path | Use the typed SDK action. |
135
+ | Adding `/api` to EAX URL | EAX base is not the REST base; app code should not build it. |
136
+ | Using `onStreamChunk`/`onTextChunk` | Use `onMessageChunk`. |
137
+ | Treating `run_error` as a successful result | Catch `WorkflowExecutionError` and inspect `outcome`. |
138
+ | Parsing `event.payload.result` | Read `event.message`; `run_complete` message is the final object. |
@@ -189,7 +189,7 @@ Schema:
189
189
  "create": "scoped",
190
190
  "update": "none",
191
191
  "delete": "none",
192
- "requiredScopes": ["created_by_user_id"],
192
+ "requiredScopes": ["submissionKey"],
193
193
  "allowedFilters": [],
194
194
  "allowedSorts": [],
195
195
  "defaultLimit": 1,
@@ -203,7 +203,7 @@ Frontend:
203
203
 
204
204
  ```ts
205
205
  await howone.public.entities.Feedback.create({
206
- created_by_user_id: projectUserId,
206
+ submissionKey,
207
207
  message,
208
208
  rating,
209
209
  })
@@ -211,7 +211,8 @@ await howone.public.entities.Feedback.create({
211
211
 
212
212
  Rules:
213
213
 
214
- - Public create needs a clear `created_by_user_id` source when backend requires ownership mapping.
214
+ - Public create must use the declared public scope/business fields. Never pass ownership/system
215
+ fields such as `created_by_user_id`; the SDK rejects them and the backend derives ownership/scope.
215
216
  - Do not expose public read unless needed.
216
217
  - Add anti-abuse UX/server constraints outside the dynamic schema when needed.
217
218
  - Never persist UI-only fields from form components.