@swirls/looms 0.3.0 → 0.4.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @swirls/looms
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 46be9ee: `LoomsRegister`, `RegisteredEvent`, and `AnyEventEnvelope` now live in `@swirls/looms/core`; `@swirls/looms/react` re-exports them, so existing `declare module '@swirls/looms/react'` registrations keep working. Standalone `defineProjection` reducers receive the registered event union, so client-only projections are typed without annotations. Registered event payloads were previously typed as `any`; they now carry their catalog types, so code that relied on `any` may need to narrow on `event.type` first.
8
+ - 46be9ee: `createFold`, `useEventFold`, `EventFoldDefinition`, and `foldEvents` in `@swirls/looms/react` are now `defineLiveFold`, `useLiveFold`, `LiveFoldDefinition`, and `foldLive(fold, events)`. A live fold always sees ephemeral events, so the `includeEphemeral` option is gone; use a projection for anything that does not need them. Live folds take an optional `shape` schema that infers their state type, and passing a projection where a live fold is expected is a type error.
9
+
10
+ Live folds no longer see ephemeral events from an effect attempt that the runtime retried, so streamed text from a failed attempt disappears without reducer code. `@swirls/looms/core` exports the rule as `dropSupersededAttempts(events)`, with `retriedEffectId(event)`.
11
+
3
12
  ## 0.3.0
4
13
 
5
14
  ### Minor Changes
@@ -3,9 +3,10 @@ import { Bot, CornerDownRight, GitFork, MessageSquare, Settings2, User, Wrench }
3
3
  import { useMemo, useState } from 'react';
4
4
  import { createEventId, isJsonObject, isJsonString, } from '@swirls/looms/core';
5
5
  import { Button, cn, Composer, EmptyState, errorMessage, FollowList, inputForm, JsonTree, messageInput, parseStructuredJson, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, shortId, useDebugger, useStartRun, } from '@swirls/looms/debugger';
6
- import { createFold, useEventFold, useProjection, useRunStore, useRunSummary } from '@swirls/looms/react';
6
+ import { useLiveFold, useProjection, useRunStore, useRunSummary } from '@swirls/looms/react';
7
7
  import { conversation, turnActivity } from '../projections.js';
8
8
  import { agentSessionMessageEvents, stopAgentTurn, userMessage } from '../signals.js';
9
+ import { streamingText } from './streaming-text.js';
9
10
  /** Child thread of the session turn that is still open, from the events the store already has. */
10
11
  function activeSessionChild(events) {
11
12
  let child;
@@ -26,34 +27,6 @@ function activeSessionChild(events) {
26
27
  }
27
28
  return child;
28
29
  }
29
- const streamingFold = createFold({
30
- name: 'agentStreamingText',
31
- initialState: { lastMessageSeq: -1, turnStarted: false, parts: [] },
32
- includeEphemeral: true,
33
- reduce(state, event) {
34
- if (event.type === 'agent.message' ||
35
- event.type === 'agent.turn.stopped' ||
36
- event.type === 'runtime.thread.cancelled') {
37
- return { lastMessageSeq: event.seq, turnStarted: false, parts: [] };
38
- }
39
- if (event.seq <= state.lastMessageSeq) {
40
- return state;
41
- }
42
- if (event.type === 'agent.turn.started') {
43
- return { ...state, turnStarted: true };
44
- }
45
- if (event.type === 'agent.turn.text_delta' &&
46
- isJsonObject(event.payload) &&
47
- isJsonString(event.payload.delta)) {
48
- return {
49
- ...state,
50
- turnStarted: true,
51
- parts: state.parts.concat(event.payload.delta),
52
- };
53
- }
54
- return state;
55
- },
56
- });
57
30
  const deliveryOptions = [
58
31
  { value: 'followUp', label: 'Follow up', description: 'Queue after the current turn' },
59
32
  { value: 'steer', label: 'Steer', description: 'Redirect the active turn' },
@@ -97,7 +70,7 @@ function StreamingItem({ text }) {
97
70
  function Transcript({ runId }) {
98
71
  const store = useRunStore(runId);
99
72
  const convo = useProjection(store, conversation);
100
- const streaming = useEventFold(store, streamingFold);
73
+ const streaming = useLiveFold(store, streamingText);
101
74
  const stream = streaming.turnStarted ? streaming.parts.join('') : null;
102
75
  const toolNames = useMemo(() => {
103
76
  const names = new Map();
@@ -0,0 +1,6 @@
1
+ /** Text the active turn has streamed since the last durable agent message. */
2
+ export declare const streamingText: import("@swirls/looms/react").LiveFoldDefinition<{
3
+ readonly lastMessageSeq: number;
4
+ readonly turnStarted: boolean;
5
+ readonly parts: readonly string[];
6
+ }, import("@swirls/looms/core").EventEnvelope>;
@@ -0,0 +1,33 @@
1
+ import { Schema } from 'effect';
2
+ import { isJsonObject, isJsonString } from '@swirls/looms/core';
3
+ import { defineLiveFold } from '@swirls/looms/react';
4
+ const StreamingTextSchema = Schema.Struct({
5
+ lastMessageSeq: Schema.Finite,
6
+ turnStarted: Schema.Boolean,
7
+ parts: Schema.Array(Schema.String),
8
+ });
9
+ /** Text the active turn has streamed since the last durable agent message. */
10
+ export const streamingText = defineLiveFold({
11
+ name: 'agentStreamingText',
12
+ shape: StreamingTextSchema,
13
+ initialState: { lastMessageSeq: -1, turnStarted: false, parts: [] },
14
+ reduce(state, event) {
15
+ if (event.type === 'agent.message' ||
16
+ event.type === 'agent.turn.stopped' ||
17
+ event.type === 'runtime.thread.cancelled') {
18
+ return { lastMessageSeq: event.seq, turnStarted: false, parts: [] };
19
+ }
20
+ if (event.seq <= state.lastMessageSeq) {
21
+ return state;
22
+ }
23
+ if (event.type === 'agent.turn.started') {
24
+ return { ...state, turnStarted: true };
25
+ }
26
+ if (event.type === 'agent.turn.text_delta' &&
27
+ isJsonObject(event.payload) &&
28
+ isJsonString(event.payload.delta)) {
29
+ return { ...state, turnStarted: true, parts: state.parts.concat(event.payload.delta) };
30
+ }
31
+ return state;
32
+ },
33
+ });