howone 0.1.27 → 0.1.29

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "howone",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "private": false,
5
5
  "description": "HowOne command line tools for creating app templates.",
6
6
  "type": "module",
@@ -62,27 +62,14 @@ type AiActionConfig<TInput, TOutput> = {
62
62
  ```ts
63
63
  type AiResult = {
64
64
  success: boolean
65
+ runId?: string
65
66
  /** Terminal outcome of the run */
66
67
  outcome: 'success' | 'credit_insufficient' | 'run_error' | null
67
- finalResult: Record<string, unknown> | null // the workflow output payload (from run_complete)
68
- /** Accumulated state data from state_update / state_snapshot events */
69
- stateData: Record<string, unknown>
70
- nodeExecutions: Array<{
71
- nodeName: string
72
- agentName?: string
73
- timestamp: number
74
- }>
75
- toolExecutions: Array<{
76
- toolCallId: string
77
- toolName: string
78
- args?: Record<string, unknown>
79
- result?: unknown
80
- durationMs?: number
81
- cost?: number
82
- timestamp: number
83
- }>
68
+ finalResult: Record<string, unknown> | null // run_complete.message
69
+ progressLogs: string[] // progress.message lines
84
70
  totalDuration: number
85
71
  errors: string[]
72
+ events: AiEvent[]
86
73
  }
87
74
  ```
88
75
 
@@ -98,42 +85,20 @@ type AiSession = {
98
85
 
99
86
  ### AiEvent (SSE events)
100
87
 
101
- All events share a common base, then carry a typed `payload`:
88
+ All workflow execute SSE events use the current envelope shape:
102
89
 
103
90
  ```ts
104
91
  type AiEvent = {
105
- type: string // see channel catalog below
106
92
  id: string
107
- run_id: string
108
- workflow_id: string
109
- timestamp: string // ISO 8601 UTC
110
- seq: number // monotonic ordering within the run
111
- // Context fields — present when applicable
112
- node_name?: string
113
- node_status?: 'pending' | 'running' | 'completed' | 'failed'
114
- action_step?: number
115
- agent_name?: string
116
- action_name?: string
117
- agent_loop_step?: number
118
- message_id?: string
119
- tool_call_id?: string
120
- payload?: Record<string, unknown>
93
+ type: 'run_start' | 'progress' | 'run_complete' | 'run_error' | 'credit_insufficient'
94
+ event: AiEvent['type']
95
+ message: string | Record<string, unknown>
96
+ payload?: Record<string, unknown> // present for run_complete as a compatibility alias of message
121
97
  }
122
98
  ```
123
99
 
124
- **SSE Channels and event types:**
125
-
126
- | Channel | Key event types |
127
- |---|---|
128
- | `lifecycle` | `run_start`, `run_complete`, `credit_insufficient`, `run_error` |
129
- | `node` | `node_scheduled`, `node_start`, `node_complete`, `node_failed` |
130
- | `action` | `action_scheduled`, `action_start`, `action_complete` |
131
- | `messages` | `ai_message_start`, `ai_message_chunk`, `ai_message_end`, `action_output` |
132
- | `tools` | `tool_call_start`, `tool_call_end`, `tool_call_error` |
133
- | `state` | `state_update`, `state_snapshot` |
134
- | `metadata` | `progress` |
135
-
136
100
  Stream terminates after exactly one of: `run_complete`, `credit_insufficient`, or `run_error`.
101
+ For the full wire protocol, read `03-sdk/09-workflow-execute-sse.md`.
137
102
 
138
103
  ---
139
104
 
@@ -230,17 +195,12 @@ When an action omits `outputSchema`, `run()` returns the raw `AiResult` executio
230
195
 
231
196
  ```ts
232
197
  const result = await howone.ai.generateStory.run(input, {
233
- onMessageChunk: (text) => {
234
- console.log('chunk:', text)
235
- },
236
- onNodeStart: (event) => {
237
- console.log(`Node ${event.node_name} started`)
238
- },
239
- onStateUpdate: (delta) => {
240
- console.log('State delta:', delta)
198
+ onMessageChunk: (line) => {
199
+ appendLog(line)
241
200
  },
242
- onProgress: (percent) => {
243
- setProgress(percent)
201
+ onProgress: (percent, line) => {
202
+ if (line?.startsWith('[DISPLAY]')) setStatus(line.replace('[DISPLAY]', '').trim())
203
+ if (percent === 100) setProgress(100)
244
204
  },
245
205
  onError: (error) => {
246
206
  console.error('SSE error:', error)
@@ -302,27 +262,20 @@ const result = await session.result
302
262
  async function consumeEvents(input: GenerateStoryInput) {
303
263
  for await (const event of howone.ai.generateStory.events(input)) {
304
264
  switch (event.type) {
305
- case 'ai_message_chunk':
306
- // streaming LLM text delta
307
- setOutput(prev => prev + (event.payload?.content_block?.text ?? event.payload?.content ?? ''))
265
+ case 'run_start':
266
+ setStatus('running')
308
267
  break
309
- case 'node_start':
310
- console.log('Node started:', event.node_name)
311
- break
312
- case 'tool_call_end':
313
- console.log('Tool result:', event.tool_call_id, event.payload?.result)
314
- break
315
- case 'state_update':
316
- console.log('State delta:', event.payload?.delta)
268
+ case 'progress':
269
+ appendLog(String(event.message))
317
270
  break
318
271
  case 'run_complete':
319
- console.log('Final result:', event.payload?.result)
272
+ console.log('Final result:', event.message)
320
273
  break
321
274
  case 'credit_insufficient':
322
- showCreditError(event.payload?.details?.reason)
275
+ showCreditError(String(event.message))
323
276
  break
324
277
  case 'run_error':
325
- showExecutionError(event.payload?.details?.reason)
278
+ showExecutionError(String(event.message))
326
279
  break
327
280
  }
328
281
  }
@@ -397,7 +350,7 @@ export const analyzeDataInputSchema = z.object({
397
350
 
398
351
  ```ts
399
352
  type SSEExecutionOptions = {
400
- // Called for every parsed event with its SSE channel
353
+ // Called for every parsed workflow envelope. channel is always "workflow".
401
354
  onEvent?: (event: AiEvent, channel: string) => void
402
355
 
403
356
  // Lifecycle
@@ -408,23 +361,10 @@ type SSEExecutionOptions = {
408
361
  /** Workflow logic failed — show retry/support UI */
409
362
  onRunError?: (event: RunErrorEvent) => void
410
363
 
411
- // Nodes
412
- onNodeStart?: (event: NodeStartEvent) => void
413
- onNodeComplete?: (event: NodeCompleteEvent) => void
414
-
415
- // Messages — streaming LLM text
416
- onMessageChunk?: (text: string, event: AiMessageChunkEvent) => void
417
- onMessageEnd?: (event: AiMessageEndEvent) => void
418
-
419
- // Tools
420
- onToolCallStart?: (event: ToolCallStartEvent) => void
421
- onToolCallEnd?: (event: ToolCallEndEvent) => void
422
- onToolCallError?: (event: ToolCallErrorEvent) => void
423
-
424
- // State — live output panel updates
425
- onStateUpdate?: (delta: Record<string, unknown>, event: StateUpdateEvent) => void
364
+ // Progress log lines from progress.message
365
+ onMessageChunk?: (text: string, event: ProgressEvent) => void
426
366
 
427
- // Progress percent 0-100
367
+ // Progress compatibility callback: progress lines fire as (0, message), success fires as (100)
428
368
  onProgress?: (percent: number, message?: string) => void
429
369
 
430
370
  // Internal transport log
@@ -448,6 +388,8 @@ type SSEExecutionOptions = {
448
388
  }
449
389
  ```
450
390
 
391
+ > The current workflow execute SSE endpoint does not emit `node_start`, `tool_call_end`,
392
+ > `state_update`, or `ai_message_chunk`. Do not write generated app code that expects those events.
451
393
  > `onCreditInsufficient` and `onRunError` are **mutually exclusive** terminal events.
452
394
  > Do not use a generic `onError` to distinguish them — use the dedicated callbacks.
453
395
 
@@ -0,0 +1,105 @@
1
+ # Workflow Execute SSE
2
+
3
+ Use this reference for `@howone/sdk` AI action streaming and raw workflow execution calls.
4
+
5
+ ## Endpoint Contract
6
+
7
+ The SDK uses the current workflow execute SSE endpoint:
8
+
9
+ | Method | Path |
10
+ |---|---|
11
+ | `POST` | `/workflow/execute/{project_short_id}/{config_id}` |
12
+
13
+ The request body is:
14
+
15
+ ```json
16
+ {
17
+ "inputs": {},
18
+ "priority": "normal"
19
+ }
20
+ ```
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`.
33
+
34
+ ## Wire Format
35
+
36
+ Frames contain only a `data:` line and a blank line:
37
+
38
+ ```text
39
+ data: {"id":"evt_8ae6d6e8a4ea","event":"run_start","message":"execution with sse is started"}
40
+
41
+ data: {"id":"evt_241ff985450b","event":"progress","message":"[DISPLAY] Executing node: Extract"}
42
+
43
+ data: {"id":"evt_f3a1b2c3d4e5","event":"run_complete","message":{"video_url":"https://..."}}
44
+ ```
45
+
46
+ There are no separate SSE `event:` or `id:` lines. The JSON envelope owns those fields.
47
+
48
+ ## Envelope
49
+
50
+ ```ts
51
+ type WorkflowSseEnvelope = {
52
+ id: string
53
+ event: 'run_start' | 'progress' | 'run_complete' | 'run_error' | 'credit_insufficient'
54
+ message: string | Record<string, unknown>
55
+ }
56
+ ```
57
+
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
+ 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.
74
+
75
+ `AiResult` maps the stream as:
76
+
77
+ ```ts
78
+ type AiResult = {
79
+ success: boolean
80
+ runId?: string
81
+ outcome: 'success' | 'credit_insufficient' | 'run_error' | null
82
+ finalResult: Record<string, unknown> | null
83
+ progressLogs: string[]
84
+ errors: string[]
85
+ events: AiEvent[]
86
+ }
87
+ ```
88
+
89
+ For typed actions with an `outputSchema`, `.run()` returns the validated `run_complete.message`
90
+ object directly.
91
+
92
+ ## Callback Rules
93
+
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
+
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.
105
+
@@ -6,6 +6,9 @@ workflow through `@howone/sdk`.
6
6
  This file answers: **how does `.howone/ai/manifest.json` become `src/lib/sdk.ts`, and how should UI
7
7
  call it?**
8
8
 
9
+ For live stream wire details, read `03-sdk/09-workflow-execute-sse.md`. The current endpoint emits
10
+ only `run_start`, `progress`, `run_complete`, `run_error`, and `credit_insufficient`.
11
+
9
12
  ## Binding Source
10
13
 
11
14
  Generate `src/lib/sdk.ts` from `.howone/ai/manifest.json`. Do not write AI bindings from memory,
@@ -117,11 +120,13 @@ Use `.stream()` when UI needs live output or cancellation:
117
120
 
118
121
  ```ts
119
122
  const session = howone.ai.generateStory.stream(input, {
120
- onMessageChunk: (text) => setDraft((prev) => prev + text),
121
- onStateUpdate: (delta) => updateLivePanel(delta),
122
- onProgress: (percent) => setProgress(percent),
123
- onCreditInsufficient: (event) => showCreditError(event.payload?.details?.reason),
124
- onRunError: (event) => showExecutionError(event.payload?.details?.reason),
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),
125
130
  onError: (error) => setError(error.message),
126
131
  onComplete: (result) => setRawResult(result),
127
132
  })
@@ -134,20 +139,17 @@ Use `.events()` when code wants an async iterable:
134
139
 
135
140
  ```ts
136
141
  for await (const event of howone.ai.generateStory.events(input)) {
137
- if (event.type === 'ai_message_chunk') {
138
- appendText(String(event.payload?.content_block?.text ?? event.payload?.content ?? ''))
139
- }
140
- if (event.type === 'state_update') {
141
- updateLivePanel(event.payload?.delta ?? {})
142
+ if (event.type === 'progress') {
143
+ appendLog(String(event.message))
142
144
  }
143
145
  if (event.type === 'run_complete') {
144
- setFinalResult(event.payload?.result)
146
+ setFinalResult(event.message)
145
147
  }
146
148
  if (event.type === 'credit_insufficient') {
147
- showCreditError(event.payload?.details?.reason)
149
+ showCreditError(String(event.message))
148
150
  }
149
151
  if (event.type === 'run_error') {
150
- showExecutionError(event.payload?.details?.reason)
152
+ showExecutionError(String(event.message))
151
153
  }
152
154
  }
153
155
  ```
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: howone-sdk
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.*.
2
+ name: howone
3
+ description: Operating manual for HowOne generated app architecture and runtime contracts. Use when a task touches app generation flow, backend/dynamic entity schema design, data access, SDK bindings, frontend code that uses HowOne data, auth, uploads, public/private access, AI capabilities/workflows, AI result persistence, realtime/status/event flows, or any @howone/sdk / howone.entities / howone.public / howone.ai calls. Load first, then read the smallest relevant numbered track: 01-architect, 02-database, 03-sdk, or 04-ai.
4
4
  ---
5
5
 
6
6
  # HowOne App Architecture Skill
@@ -50,7 +50,7 @@ Read references by task shape. Prefer exact references over generic examples.
50
50
  - File upload: read `03-sdk/05-file-upload.md`.
51
51
  - Raw HTTP escape hatch: read `03-sdk/06-raw-http.md`.
52
52
  - App-side AI action calls after `.howone/ai/manifest.json` exists: read
53
- `03-sdk/07-ai-action-calls.md`.
53
+ `03-sdk/07-ai-action-calls.md` and `03-sdk/09-workflow-execute-sse.md`.
54
54
  - External workflow create/update/status: read `04-ai/05-workflow-operations.md`.
55
55
  - Common AI feature templates: read `04-ai/06-ai-feature-playbooks.md`.
56
56
 
@@ -71,7 +71,7 @@ Before writing code, classify the touched surfaces:
71
71
  | AI capability or workflow design | `04-ai/01-ai-capability-architecture.md`, `04-ai/04-service-capability-catalog.md`, `04-ai/02-workflow-contract-rules.md` |
72
72
  | External workflow create/update/status | `04-ai/05-workflow-operations.md` |
73
73
  | Common AI feature examples | `04-ai/06-ai-feature-playbooks.md` |
74
- | AI manifest handoff to app code | `04-ai/03-ai-sdk-handoff.md`, `03-sdk/07-ai-action-calls.md` |
74
+ | AI manifest handoff to app code | `04-ai/03-ai-sdk-handoff.md`, `03-sdk/07-ai-action-calls.md`, `03-sdk/09-workflow-execute-sse.md` |
75
75
  | File upload | `03-sdk/05-file-upload.md` |
76
76
  | Raw HTTP escape hatch | `03-sdk/06-raw-http.md` |
77
77
  | SDK extensibility / adapter boundaries | `03-sdk/08-extension-boundaries.md` |
@@ -14,7 +14,7 @@
14
14
  "dependencies": {
15
15
  "@base-ui/react": "^1.4.1",
16
16
  "@fontsource-variable/inter": "^5.2.8",
17
- "@howone/sdk": "2.0.0-beta.20",
17
+ "@howone/sdk": "2.0.0-beta.21",
18
18
  "@tailwindcss/vite": "^4.2.1",
19
19
  "class-variance-authority": "^0.7.1",
20
20
  "clsx": "^2.1.1",