howone 0.1.25 → 0.1.27

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 (26) hide show
  1. package/package.json +1 -1
  2. package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/07-ai-action-calls.md +102 -48
  3. package/templates/vite/.howone/skills/{howone-sdk → howone}/04-ai/03-ai-sdk-handoff.md +19 -4
  4. package/templates/vite/package.json +1 -1
  5. package/templates/vite/src/lib/sdk.ts +1 -1
  6. /package/templates/vite/.howone/skills/{howone-sdk → howone}/01-architect/01-app-generation.md +0 -0
  7. /package/templates/vite/.howone/skills/{howone-sdk → howone}/01-architect/02-manifest-codegen.md +0 -0
  8. /package/templates/vite/.howone/skills/{howone-sdk → howone}/02-database/01-schema-design.md +0 -0
  9. /package/templates/vite/.howone/skills/{howone-sdk → howone}/02-database/02-schema-operations.md +0 -0
  10. /package/templates/vite/.howone/skills/{howone-sdk → howone}/02-database/03-data-access-patterns.md +0 -0
  11. /package/templates/vite/.howone/skills/{howone-sdk → howone}/02-database/04-query-dsl-and-responses.md +0 -0
  12. /package/templates/vite/.howone/skills/{howone-sdk → howone}/02-database/05-ai-persistence-patterns.md +0 -0
  13. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/01-client-setup.md +0 -0
  14. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/02-entity-operations.md +0 -0
  15. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/03-auth.md +0 -0
  16. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/04-react-integration.md +0 -0
  17. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/05-file-upload.md +0 -0
  18. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/06-raw-http.md +0 -0
  19. /package/templates/vite/.howone/skills/{howone-sdk → howone}/03-sdk/08-extension-boundaries.md +0 -0
  20. /package/templates/vite/.howone/skills/{howone-sdk → howone}/04-ai/01-ai-capability-architecture.md +0 -0
  21. /package/templates/vite/.howone/skills/{howone-sdk → howone}/04-ai/02-workflow-contract-rules.md +0 -0
  22. /package/templates/vite/.howone/skills/{howone-sdk → howone}/04-ai/04-service-capability-catalog.md +0 -0
  23. /package/templates/vite/.howone/skills/{howone-sdk → howone}/04-ai/05-workflow-operations.md +0 -0
  24. /package/templates/vite/.howone/skills/{howone-sdk → howone}/04-ai/06-ai-feature-playbooks.md +0 -0
  25. /package/templates/vite/.howone/skills/{howone-sdk → howone}/SKILL.md +0 -0
  26. /package/templates/vite/.howone/skills/{howone-sdk → howone}/agents/openai.yaml +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "howone",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "private": false,
5
5
  "description": "HowOne command line tools for creating app templates.",
6
6
  "type": "module",
@@ -62,17 +62,23 @@ type AiActionConfig<TInput, TOutput> = {
62
62
  ```ts
63
63
  type AiResult = {
64
64
  success: boolean
65
- finalResult: Record<string, unknown> | null // the workflow output payload
65
+ /** Terminal outcome of the run */
66
+ 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>
66
70
  nodeExecutions: Array<{
67
71
  nodeName: string
68
- content: string
72
+ agentName?: string
69
73
  timestamp: number
70
74
  }>
71
- costUpdates: Array<{
72
- token: number
73
- totalToken: number
74
- cost: number
75
- totalCost: number
75
+ toolExecutions: Array<{
76
+ toolCallId: string
77
+ toolName: string
78
+ args?: Record<string, unknown>
79
+ result?: unknown
80
+ durationMs?: number
81
+ cost?: number
76
82
  timestamp: number
77
83
  }>
78
84
  totalDuration: number
@@ -92,14 +98,43 @@ type AiSession = {
92
98
 
93
99
  ### AiEvent (SSE events)
94
100
 
101
+ All events share a common base, then carry a typed `payload`:
102
+
95
103
  ```ts
96
104
  type AiEvent = {
97
- type: string
98
- data?: Record<string, unknown>
99
- [key: string]: unknown
105
+ type: string // see channel catalog below
106
+ 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>
100
121
  }
101
122
  ```
102
123
 
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
+ Stream terminates after exactly one of: `run_complete`, `credit_insufficient`, or `run_error`.
137
+
103
138
  ---
104
139
 
105
140
  ## Defining AI Actions
@@ -195,17 +230,17 @@ When an action omits `outputSchema`, `run()` returns the raw `AiResult` executio
195
230
 
196
231
  ```ts
197
232
  const result = await howone.ai.generateStory.run(input, {
198
- onStreamChunk: (chunk) => {
199
- console.log('chunk:', chunk)
233
+ onMessageChunk: (text) => {
234
+ console.log('chunk:', text)
200
235
  },
201
- onNodeStart: (nodeName, content) => {
202
- console.log(`Node ${nodeName} started`)
236
+ onNodeStart: (event) => {
237
+ console.log(`Node ${event.node_name} started`)
203
238
  },
204
- onCostUpdate: (cost) => {
205
- console.log(`Tokens used: ${cost.totalToken}`)
239
+ onStateUpdate: (delta) => {
240
+ console.log('State delta:', delta)
206
241
  },
207
- onProgress: (progress) => {
208
- setProgress(progress)
242
+ onProgress: (percent) => {
243
+ setProgress(percent)
209
244
  },
210
245
  onError: (error) => {
211
246
  console.error('SSE error:', error)
@@ -237,8 +272,8 @@ try {
237
272
  ```ts
238
273
  function startStream(input: GenerateStoryInput) {
239
274
  const session = howone.ai.generateStory.stream(input, {
240
- onStreamChunk: (chunk) => {
241
- setOutput(prev => prev + chunk)
275
+ onMessageChunk: (text) => {
276
+ setOutput(prev => prev + text)
242
277
  },
243
278
  onComplete: (result) => {
244
279
  console.log('Done:', result.finalResult)
@@ -267,17 +302,27 @@ const result = await session.result
267
302
  async function consumeEvents(input: GenerateStoryInput) {
268
303
  for await (const event of howone.ai.generateStory.events(input)) {
269
304
  switch (event.type) {
270
- case 'stream_content':
271
- setOutput(prev => prev + (event.data?.delta ?? ''))
305
+ case 'ai_message_chunk':
306
+ // streaming LLM text delta
307
+ setOutput(prev => prev + (event.payload?.content_block?.text ?? event.payload?.content ?? ''))
272
308
  break
273
309
  case 'node_start':
274
- console.log('Node started:', event.data?.nodeName)
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)
275
314
  break
276
- case 'cost_update':
277
- console.log('Cost:', event.data?.totalCost)
315
+ case 'state_update':
316
+ console.log('State delta:', event.payload?.delta)
278
317
  break
279
- case 'complete':
280
- console.log('Final result:', event.data)
318
+ case 'run_complete':
319
+ console.log('Final result:', event.payload?.result)
320
+ break
321
+ case 'credit_insufficient':
322
+ showCreditError(event.payload?.details?.reason)
323
+ break
324
+ case 'run_error':
325
+ showExecutionError(event.payload?.details?.reason)
281
326
  break
282
327
  }
283
328
  }
@@ -352,37 +397,43 @@ export const analyzeDataInputSchema = z.object({
352
397
 
353
398
  ```ts
354
399
  type SSEExecutionOptions = {
355
- // Called for every raw SSE event
356
- onEvent?: (event: { type: string; data?: Record<string, unknown> }) => void
400
+ // Called for every parsed event with its SSE channel
401
+ onEvent?: (event: AiEvent, channel: string) => void
357
402
 
358
- // Called when a workflow node starts executing
359
- onNodeStart?: (nodeName: string, content: string) => void
403
+ // Lifecycle
404
+ onRunStart?: (event: RunStartEvent) => void
405
+ onRunComplete?: (event: RunCompleteEvent, result: AiResult) => void
406
+ /** Credits / quota exhausted — show top-up UI, NOT a generic error */
407
+ onCreditInsufficient?: (event: CreditInsufficientEvent) => void
408
+ /** Workflow logic failed — show retry/support UI */
409
+ onRunError?: (event: RunErrorEvent) => void
360
410
 
361
- // Called with streaming text delta (for LLM text generation nodes)
362
- onStreamContent?: (delta: string) => void
411
+ // Nodes
412
+ onNodeStart?: (event: NodeStartEvent) => void
413
+ onNodeComplete?: (event: NodeCompleteEvent) => void
363
414
 
364
- // Called with each raw stream chunk
365
- onStreamChunk?: (chunk: string) => void
415
+ // Messages streaming LLM text
416
+ onMessageChunk?: (text: string, event: AiMessageChunkEvent) => void
417
+ onMessageEnd?: (event: AiMessageEndEvent) => void
366
418
 
367
- // Called when token/cost counters update
368
- onCostUpdate?: (cost: {
369
- token: number
370
- totalToken: number
371
- cost: number
372
- totalCost: number
373
- timestamp: number
374
- }) => void
419
+ // Tools
420
+ onToolCallStart?: (event: ToolCallStartEvent) => void
421
+ onToolCallEnd?: (event: ToolCallEndEvent) => void
422
+ onToolCallError?: (event: ToolCallErrorEvent) => void
375
423
 
376
- // Called with an estimated progress value (0–100)
377
- onProgress?: (progress: number) => void
424
+ // State live output panel updates
425
+ onStateUpdate?: (delta: Record<string, unknown>, event: StateUpdateEvent) => void
378
426
 
379
- // Called with log messages from workflow nodes
427
+ // Progress percent 0-100
428
+ onProgress?: (percent: number, message?: string) => void
429
+
430
+ // Internal transport log
380
431
  onLog?: (message: string) => void
381
432
 
382
- // Called if an error occurs
433
+ // Called on any error (credit or execution)
383
434
  onError?: (error: Error) => void
384
435
 
385
- // Called when the workflow completes with the raw execution envelope
436
+ // Called when the stream closes (success or error)
386
437
  onComplete?: (result: AiResult) => void
387
438
 
388
439
  // Abort signal — connect to an AbortController for cancellation
@@ -397,6 +448,9 @@ type SSEExecutionOptions = {
397
448
  }
398
449
  ```
399
450
 
451
+ > `onCreditInsufficient` and `onRunError` are **mutually exclusive** terminal events.
452
+ > Do not use a generic `onError` to distinguish them — use the dedicated callbacks.
453
+
400
454
  ---
401
455
 
402
456
  ## AiSchemaValidationError
@@ -117,8 +117,11 @@ Use `.stream()` when UI needs live output or cancellation:
117
117
 
118
118
  ```ts
119
119
  const session = howone.ai.generateStory.stream(input, {
120
- onStreamChunk: (chunk) => setDraft((prev) => prev + chunk),
121
- onProgress: (progress) => setProgress(progress),
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),
122
125
  onError: (error) => setError(error.message),
123
126
  onComplete: (result) => setRawResult(result),
124
127
  })
@@ -131,8 +134,20 @@ Use `.events()` when code wants an async iterable:
131
134
 
132
135
  ```ts
133
136
  for await (const event of howone.ai.generateStory.events(input)) {
134
- if (event.type === 'stream_content') {
135
- appendText(String(event.data?.delta ?? ''))
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
+ }
143
+ if (event.type === 'run_complete') {
144
+ setFinalResult(event.payload?.result)
145
+ }
146
+ if (event.type === 'credit_insufficient') {
147
+ showCreditError(event.payload?.details?.reason)
148
+ }
149
+ if (event.type === 'run_error') {
150
+ showExecutionError(event.payload?.details?.reason)
136
151
  }
137
152
  }
138
153
  ```
@@ -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.18",
17
+ "@howone/sdk": "2.0.0-beta.20",
18
18
  "@tailwindcss/vite": "^4.2.1",
19
19
  "class-variance-authority": "^0.7.1",
20
20
  "clsx": "^2.1.1",
@@ -22,7 +22,7 @@ export const ai = defineAiActions({
22
22
  // Do not paste JSON Schema objects from .howone/ai/manifest.json here directly.
23
23
  // With outputSchema configured, howone.ai.<action>.run() returns the validated finalResult payload.
24
24
  // generateImage: defineAiAction("generateImage", {
25
- // workflowId: "<workflow-uuid>", // workflow ID for this capability
25
+ // workflowId: "<workflow-config-id>", // config_id for this capability
26
26
  // inputSchema: generateImageInputSchema,
27
27
  // outputSchema: generateImageOutputSchema,
28
28
  // }),