howone 0.1.28 → 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 +1 -1
- package/templates/vite/.howone/skills/howone/03-sdk/07-ai-action-calls.md +28 -86
- package/templates/vite/.howone/skills/howone/03-sdk/09-workflow-execute-sse.md +105 -0
- package/templates/vite/.howone/skills/howone/04-ai/03-ai-sdk-handoff.md +15 -13
- package/templates/vite/.howone/skills/howone/SKILL.md +2 -2
- package/templates/vite/package.json +1 -1
package/package.json
CHANGED
|
@@ -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 //
|
|
68
|
-
|
|
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
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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: (
|
|
234
|
-
|
|
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
|
-
|
|
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 '
|
|
306
|
-
|
|
307
|
-
setOutput(prev => prev + (event.payload?.content_block?.text ?? event.payload?.content ?? ''))
|
|
265
|
+
case 'run_start':
|
|
266
|
+
setStatus('running')
|
|
308
267
|
break
|
|
309
|
-
case '
|
|
310
|
-
|
|
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.
|
|
272
|
+
console.log('Final result:', event.message)
|
|
320
273
|
break
|
|
321
274
|
case 'credit_insufficient':
|
|
322
|
-
showCreditError(event.
|
|
275
|
+
showCreditError(String(event.message))
|
|
323
276
|
break
|
|
324
277
|
case 'run_error':
|
|
325
|
-
showExecutionError(event.
|
|
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
|
|
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
|
-
//
|
|
412
|
-
|
|
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
|
|
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: (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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 === '
|
|
138
|
-
|
|
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.
|
|
146
|
+
setFinalResult(event.message)
|
|
145
147
|
}
|
|
146
148
|
if (event.type === 'credit_insufficient') {
|
|
147
|
-
showCreditError(event.
|
|
149
|
+
showCreditError(String(event.message))
|
|
148
150
|
}
|
|
149
151
|
if (event.type === 'run_error') {
|
|
150
|
-
showExecutionError(event.
|
|
152
|
+
showExecutionError(String(event.message))
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
```
|
|
@@ -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.
|
|
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",
|