Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/remove-use-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@perstack/react": patch
---

Remove unused `useRuntime` hook and related types from public API

The `useRuntime` hook was not used in any actual code. Runtime information display is handled by application-specific hooks.

Removed exports:
- `useRuntime` hook
- `RuntimeResult`, `RuntimeState`, `SkillState`, `DockerBuildState`, `DockerContainerState`, `ProxyAccessState` types
- `createInitialRuntimeState` function
28 changes: 12 additions & 16 deletions docs/references/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,37 +480,33 @@ rl.on("line", (line) => {
Use the provided hooks from `@perstack/react`:

```typescript
import { useRun, useRuntime } from "@perstack/react"
import { useRun } from "@perstack/react"

function ExpertRunner() {
// RunEvents → accumulated activities + streaming state
const { activities, streaming, addEvent } = useRun()

// RuntimeEvents → current runtime environment state
const { runtimeState, handleRuntimeEvent } = useRuntime()

const handleEvent = (event: PerstackEvent) => {
// Try RuntimeEvent first (returns false if not handled)
if (!handleRuntimeEvent(event)) {
// Must be RunEvent, add to run state
addEvent(event)
const { activities, streaming, isComplete, addEvent } = useRun()

useEffect(() => {
const eventSource = new EventSource("/api/events")
eventSource.onmessage = (e) => {
addEvent(JSON.parse(e.data))
}
}
return () => eventSource.close()
}, [addEvent])

return (
<div>
{/* Show current runtime state */}
<RuntimeStatus state={runtimeState} />

{/* Show streaming content (grouped by run for parallel execution) */}
{Object.entries(streaming.runs).map(([runId, run]) => (
run.isReasoningActive && (
<ReasoningDisplay key={runId} expertKey={run.expertKey} text={run.reasoning} />
)
))}

{/* Show accumulated activities */}
<ActivityLog activities={activities} />

{isComplete && <div>Run complete!</div>}
</div>
)
}
Expand Down
123 changes: 34 additions & 89 deletions packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ pnpm add @perstack/react

## Usage

### useLogStore
### useRun

The main hook for managing Perstack events. It separates events into:
- **LogEntry[]** - Accumulated log from RunEvent (state machine transitions)
- **RuntimeState** - Current state from RuntimeEvent (runtime environment)
The main hook for managing Perstack run state. It processes events into:

- **activities** - Accumulated activities from RunEvent (append-only)
- **streaming** - Current streaming state for real-time display

```tsx
import { useLogStore } from "@perstack/react"
import { useRun } from "@perstack/react"

function MyComponent() {
const { logs, runtimeState, isComplete, eventCount, addEvent, appendHistoricalEvents } =
useLogStore()
function ExpertRunner() {
const { activities, streaming, isComplete, addEvent, appendHistoricalEvents } = useRun()

// Add events from your event source
useEffect(() => {
Expand All @@ -36,19 +36,17 @@ function MyComponent() {

return (
<div>
{logs.map((entry) => (
<LogRow key={entry.id} action={entry.action} />
))}
{Object.entries(runtimeState.streaming.runs).map(([runId, run]) => (
<div key={runId}>
{run.isReasoningActive && (
<div>[{run.expertKey}] Reasoning: {run.reasoning}</div>
)}
{run.isRunResultActive && (
<div>[{run.expertKey}] Generating: {run.runResult}</div>
)}
</div>
{/* Show streaming content (grouped by run for parallel execution) */}
{Object.entries(streaming.runs).map(([runId, run]) => (
run.isReasoningActive && (
<div key={runId}>[{run.expertKey}] Reasoning: {run.reasoning}</div>
)
))}

{/* Show accumulated activities */}
<ActivityLog activities={activities} />

{isComplete && <div>Run complete!</div>}
</div>
)
}
Expand Down Expand Up @@ -89,58 +87,44 @@ function JobActivityView({ jobId, isRunning }: { jobId: string; isRunning: boole
}
```

### useRuntimeState

A lower-level hook for managing RuntimeState separately.

```tsx
import { useRuntimeState } from "@perstack/react"

function MyComponent() {
const { runtimeState, handleRuntimeEvent, clearStreaming, resetRuntimeState } = useRuntimeState()

// Returns true if the event was handled (RuntimeEvent)
// Returns false if the event should be processed elsewhere (RunEvent)
const wasHandled = handleRuntimeEvent(event)
}
```

### Utility Functions

For advanced use cases, you can use the utility functions directly:

```tsx
import {
createInitialLogProcessState,
processRunEventToLog,
toolToCheckpointAction,
createInitialActivityProcessState,
processRunEventToActivity,
toolToActivity,
groupActivitiesByRun,
} from "@perstack/react"

// Create processing state
const state = createInitialLogProcessState()
const state = createInitialActivityProcessState()

// Process RunEvent into LogEntry
const logs = []
processRunEventToLog(state, event, (entry) => logs.push(entry))
// Process RunEvent into Activity
const activities = []
processRunEventToActivity(state, event, (activity) => activities.push(activity))

// Convert a single tool call + result to CheckpointAction
const action = toolToCheckpointAction(toolCall, toolResult, reasoning)
// Group activities by run ID
const grouped = groupActivitiesByRun(activities)
```

## API

### useLogStore()
### useRun()

Returns an object with:

- `logs`: Array of `LogEntry` representing completed actions (append-only)
- `runtimeState`: Current `RuntimeState` including streaming state
- `activities`: Array of `ActivityOrGroup` representing completed actions (append-only)
- `streaming`: Current `StreamingState` for real-time display
- `isComplete`: Whether the run is complete
- `eventCount`: Total number of processed events
- `addEvent(event)`: Add a new event to process
- `appendHistoricalEvents(events)`: Append historical events to logs
- `appendHistoricalEvents(events)`: Bulk load historical events
- `clearStreaming()`: Clear streaming state

**Note:** Logs are append-only and never cleared. This is required for compatibility with Ink's `<Static>` component.
**Note:** Activities are append-only and never cleared. This is required for compatibility with Ink's `<Static>` component.

### useEventStream(options)

Expand All @@ -163,47 +147,8 @@ The hook automatically:
- Processes events through `useRun` internally
- Clears error state on reconnection

### useRuntimeState()

Returns an object with:

- `runtimeState`: Current `RuntimeState`
- `handleRuntimeEvent(event)`: Process a RuntimeEvent, returns `true` if handled
- `clearStreaming()`: Reset streaming state
- `resetRuntimeState()`: Reset entire runtime state

## Types

### LogEntry

Wraps `CheckpointAction` with an ID for React key purposes:

```typescript
type LogEntry = {
id: string
action: CheckpointAction
}
```

### RuntimeState

Captures current runtime environment state:

```typescript
type RuntimeState = {
query?: string
expertName?: string
model?: string
runtime?: string
runtimeVersion?: string
skills: Map<string, SkillState>
dockerBuild?: DockerBuildState
dockerContainers: Map<string, DockerContainerState>
proxyAccess?: ProxyAccessState
streaming: StreamingState
}
```

### StreamingState

Real-time streaming state, organized by run ID to support parallel execution:
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ export {
useEventStream,
} from "./use-event-stream.js"
export { type ActivityProcessState, type RunResult, useRun } from "./use-run.js"
export { type RuntimeResult, useRuntime } from "./use-runtime.js"
Loading