@ekairos/thread 1.22.15-beta.feature-thread-unify.0 → 1.22.17-beta.feature-core-thread-registry-sync.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/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Durable AI threads for production apps.
4
4
 
5
+ ## Specification
6
+
7
+ Normative contract and compatibility profile:
8
+
9
+ - `SPEC.md`
10
+
5
11
  `@ekairos/thread` gives you an execution model that is:
6
12
 
7
13
  - workflow-compatible,
@@ -33,7 +39,7 @@ This design supports long-running, resumable agent runs without losing state.
33
39
  - `Reactor`: pluggable reaction implementation (`AI SDK`, `Codex`, `Claude`, `Cursor`, ...).
34
40
  - `Thread Key`: stable public identifier (`thread.key`) for continuity.
35
41
  - `Context`: typed persistent state attached to a thread.
36
- - `Item`: normalized event (`input_text`, `output_text`, etc).
42
+ - `Item`: normalized timeline record (`message`, `action_execute`, `action_result`, ...).
37
43
  - `Execution`: one run for a trigger/reaction pair.
38
44
  - `Step`: one loop iteration inside an execution.
39
45
  - `Part`: normalized content fragment persisted by step.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- export { thread, createThread, createAiSdkReactor, createScriptedReactor, type CreateAiSdkReactorOptions, type CreateScriptedReactorOptions, type ScriptedReactorStep, type ThreadConfig, type ThreadInstance, type ThreadOptions, type ThreadStreamOptions, type ThreadReactor, type ThreadReactorParams, type ThreadReactionResult, type ThreadReactionToolCall, type ThreadReactionLLM, Thread, type RegistrableThreadBuilder, } from "./thread.js";
1
+ export { thread, createThread, createAiSdkReactor, createScriptedReactor, type CreateAiSdkReactorOptions, type CreateScriptedReactorOptions, type ScriptedReactorStep, type ThreadConfig, type ThreadInstance, type ThreadOptions, type ThreadStreamOptions, type ThreadReactor, type ThreadReactorParams, type ThreadReactionResult, type ThreadActionRequest, type ThreadReactionLLM, Thread, type RegistrableThreadBuilder, } from "./thread.js";
2
2
  export type { ThreadStore, ThreadIdentifier, ContextIdentifier, StoredThread, StoredContext, ThreadItem, } from "./thread.store.js";
3
3
  export type { WireDate, ThreadMirrorContext, ThreadMirrorExecution, ThreadMirrorWrite, ThreadMirrorRequest, } from "./mirror.js";
4
4
  export { registerThread, getThread, getThreadFactory, hasThread, listThreads, type ThreadKey, } from "./thread.registry.js";
5
5
  export { threadDomain } from "./schema.js";
6
6
  export { didToolExecute, extractToolCallsFromParts } from "./thread.toolcalls.js";
7
- export { INPUT_TEXT_ITEM_TYPE, OUTPUT_TEXT_ITEM_TYPE, SYSTEM_TEXT_ITEM_TYPE, WEB_CHANNEL, AGENT_CHANNEL, EMAIL_CHANNEL, createUserItemFromUIMessages, createAssistantItemFromUIMessages, convertToUIMessage, convertItemToModelMessages, convertItemsToModelMessages, convertModelMessageToItem, type ResponseMessage, } from "./thread.events.js";
8
- export { THREAD_STATUSES, THREAD_CONTEXT_STATUSES, THREAD_EXECUTION_STATUSES, THREAD_STEP_STATUSES, THREAD_ITEM_STATUSES, THREAD_ITEM_TYPES, THREAD_CHANNELS, THREAD_TRACE_EVENT_KINDS, THREAD_STREAM_CHUNK_TYPES, THREAD_CONTEXT_SUBSTATE_KEYS, THREAD_THREAD_TRANSITIONS, THREAD_CONTEXT_TRANSITIONS, THREAD_EXECUTION_TRANSITIONS, THREAD_STEP_TRANSITIONS, THREAD_ITEM_TRANSITIONS, canThreadTransition, canContextTransition, canExecutionTransition, canStepTransition, canItemTransition, assertThreadTransition, assertContextTransition, assertExecutionTransition, assertStepTransition, assertItemTransition, assertThreadPartKey, } from "./thread.contract.js";
9
- export type { Transition, ThreadThreadStatus, ThreadContextStatus, ThreadExecutionStatus, ThreadStepStatus, ThreadItemStatus, ThreadItemType, ThreadChannel, ThreadTraceEventKind, ThreadStreamChunkType, ThreadContextSubstateKey, ThreadTransition, ContextTransition, ExecutionTransition, StepTransition, ItemTransition, } from "./thread.contract.js";
7
+ export { INPUT_ITEM_TYPE, INPUT_TEXT_ITEM_TYPE, OUTPUT_ITEM_TYPE, WEB_CHANNEL, AGENT_CHANNEL, EMAIL_CHANNEL, createUserItemFromUIMessages, createAssistantItemFromUIMessages, convertToUIMessage, convertItemToModelMessages, convertItemsToModelMessages, convertModelMessageToItem, type ResponseMessage, } from "./thread.events.js";
8
+ export { THREAD_STATUSES, THREAD_CONTEXT_STATUSES, THREAD_EXECUTION_STATUSES, THREAD_STEP_STATUSES, THREAD_STEP_KINDS, THREAD_ITEM_STATUSES, THREAD_ITEM_TYPES, THREAD_CHANNELS, THREAD_TRACE_EVENT_KINDS, THREAD_STREAM_CHUNK_TYPES, THREAD_STREAM_LIFECYCLE_CHUNK_TYPES, THREAD_STREAM_TEXT_CHUNK_TYPES, THREAD_STREAM_REASONING_CHUNK_TYPES, THREAD_STREAM_ACTION_CHUNK_TYPES, THREAD_STREAM_SOURCE_CHUNK_TYPES, THREAD_STREAM_METADATA_CHUNK_TYPES, THREAD_STREAM_ERROR_CHUNK_TYPES, THREAD_THREAD_TRANSITIONS, THREAD_CONTEXT_TRANSITIONS, THREAD_EXECUTION_TRANSITIONS, THREAD_STEP_TRANSITIONS, THREAD_ITEM_TRANSITIONS, canThreadTransition, canContextTransition, canExecutionTransition, canStepTransition, canItemTransition, assertThreadTransition, assertContextTransition, assertExecutionTransition, assertStepTransition, assertItemTransition, isThreadStreamChunkType, assertThreadPartKey, } from "./thread.contract.js";
9
+ export type { Transition, ThreadThreadStatus, ThreadContextStatus, ThreadExecutionStatus, ThreadStepStatus, ThreadStepKind, ThreadItemStatus, ThreadItemType, ThreadChannel, ThreadTraceEventKind, ThreadStreamChunkType, ThreadTransition, ContextTransition, ExecutionTransition, StepTransition, ItemTransition, } from "./thread.contract.js";
10
10
  export { DEFAULT_CODEX_TOOL_NAME, DEFAULT_CODEX_MODEL, codexToolInputSchema, buildDefaultCodexNarrative, didCodexToolExecute, createCodexThreadBuilder, type CodexThreadRuntimeMode, type CodexThreadReasoningLevel, type CodexThreadRuntime, type CodexThreadEnv, type CodexToolInput, type CodexToolOutput, type CodexExecuteArgs, type CodexThreadBuilderConfig, type CodexThreadBuilder, } from "./codex.js";
11
11
  export { useThread, type ThreadSnapshot, type ThreadStreamChunk, type UseThreadOptions, } from "./react.js";
12
12
  export { parseThreadStreamEvent, assertThreadStreamTransitions, validateThreadStreamTimeline, } from "./thread.stream.js";
13
- export type { ThreadStreamEvent, ContextCreatedEvent, ContextResolvedEvent, ContextStatusChangedEvent, ThreadCreatedEvent, ThreadResolvedEvent, ThreadStatusChangedEvent, ExecutionCreatedEvent, ExecutionStatusChangedEvent, ItemCreatedEvent, ItemStatusChangedEvent, StepCreatedEvent, StepStatusChangedEvent, PartCreatedEvent, PartUpdatedEvent, ChunkEmittedEvent, ThreadFinishedEvent, } from "./thread.stream.js";
13
+ export type { ThreadStreamEvent, ContextCreatedEvent, ContextResolvedEvent, ContextOpenedEvent, ContextClosedEvent, ContextContentUpdatedEvent, ThreadCreatedEvent, ThreadResolvedEvent, ThreadStreamingStartedEvent, ThreadIdleEvent, ExecutionCreatedEvent, ExecutionCompletedEvent, ExecutionFailedEvent, ItemCreatedEvent, ItemUpdatedEvent, ItemPendingEvent, ItemCompletedEvent, StepCreatedEvent, StepUpdatedEvent, StepCompletedEvent, StepFailedEvent, PartCreatedEvent, PartUpdatedEvent, ChunkEmittedEvent, } from "./thread.stream.js";
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@ thread, createThread, createAiSdkReactor, createScriptedReactor, Thread, } from
4
4
  export { registerThread, getThread, getThreadFactory, hasThread, listThreads, } from "./thread.registry.js";
5
5
  export { threadDomain } from "./schema.js";
6
6
  export { didToolExecute, extractToolCallsFromParts } from "./thread.toolcalls.js";
7
- export { INPUT_TEXT_ITEM_TYPE, OUTPUT_TEXT_ITEM_TYPE, SYSTEM_TEXT_ITEM_TYPE, WEB_CHANNEL, AGENT_CHANNEL, EMAIL_CHANNEL, createUserItemFromUIMessages, createAssistantItemFromUIMessages, convertToUIMessage, convertItemToModelMessages, convertItemsToModelMessages, convertModelMessageToItem, } from "./thread.events.js";
8
- export { THREAD_STATUSES, THREAD_CONTEXT_STATUSES, THREAD_EXECUTION_STATUSES, THREAD_STEP_STATUSES, THREAD_ITEM_STATUSES, THREAD_ITEM_TYPES, THREAD_CHANNELS, THREAD_TRACE_EVENT_KINDS, THREAD_STREAM_CHUNK_TYPES, THREAD_CONTEXT_SUBSTATE_KEYS, THREAD_THREAD_TRANSITIONS, THREAD_CONTEXT_TRANSITIONS, THREAD_EXECUTION_TRANSITIONS, THREAD_STEP_TRANSITIONS, THREAD_ITEM_TRANSITIONS, canThreadTransition, canContextTransition, canExecutionTransition, canStepTransition, canItemTransition, assertThreadTransition, assertContextTransition, assertExecutionTransition, assertStepTransition, assertItemTransition, assertThreadPartKey, } from "./thread.contract.js";
7
+ export { INPUT_ITEM_TYPE, INPUT_TEXT_ITEM_TYPE, OUTPUT_ITEM_TYPE, WEB_CHANNEL, AGENT_CHANNEL, EMAIL_CHANNEL, createUserItemFromUIMessages, createAssistantItemFromUIMessages, convertToUIMessage, convertItemToModelMessages, convertItemsToModelMessages, convertModelMessageToItem, } from "./thread.events.js";
8
+ export { THREAD_STATUSES, THREAD_CONTEXT_STATUSES, THREAD_EXECUTION_STATUSES, THREAD_STEP_STATUSES, THREAD_STEP_KINDS, THREAD_ITEM_STATUSES, THREAD_ITEM_TYPES, THREAD_CHANNELS, THREAD_TRACE_EVENT_KINDS, THREAD_STREAM_CHUNK_TYPES, THREAD_STREAM_LIFECYCLE_CHUNK_TYPES, THREAD_STREAM_TEXT_CHUNK_TYPES, THREAD_STREAM_REASONING_CHUNK_TYPES, THREAD_STREAM_ACTION_CHUNK_TYPES, THREAD_STREAM_SOURCE_CHUNK_TYPES, THREAD_STREAM_METADATA_CHUNK_TYPES, THREAD_STREAM_ERROR_CHUNK_TYPES, THREAD_THREAD_TRANSITIONS, THREAD_CONTEXT_TRANSITIONS, THREAD_EXECUTION_TRANSITIONS, THREAD_STEP_TRANSITIONS, THREAD_ITEM_TRANSITIONS, canThreadTransition, canContextTransition, canExecutionTransition, canStepTransition, canItemTransition, assertThreadTransition, assertContextTransition, assertExecutionTransition, assertStepTransition, assertItemTransition, isThreadStreamChunkType, assertThreadPartKey, } from "./thread.contract.js";
9
9
  export { DEFAULT_CODEX_TOOL_NAME, DEFAULT_CODEX_MODEL, codexToolInputSchema, buildDefaultCodexNarrative, didCodexToolExecute, createCodexThreadBuilder, } from "./codex.js";
10
10
  export { useThread, } from "./react.js";
11
11
  export { parseThreadStreamEvent, assertThreadStreamTransitions, validateThreadStreamTimeline, } from "./thread.stream.js";
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ThreadContextStatus, ThreadContextSubstateKey, ThreadThreadStatus } from "./thread.contract.js";
1
+ import type { ThreadContextStatus, ThreadThreadStatus } from "./thread.contract.js";
2
2
  export type ThreadSnapshot<Context = unknown, Item = Record<string, unknown>> = {
3
3
  thread: {
4
4
  id: string;
@@ -17,25 +17,10 @@ export type ThreadSnapshot<Context = unknown, Item = Record<string, unknown>> =
17
17
  items: Item[];
18
18
  };
19
19
  export type ThreadStreamChunk = {
20
- type: "data-context-id";
20
+ type: `data-context.${string}`;
21
21
  data?: {
22
22
  contextId?: string;
23
23
  };
24
- id?: string;
25
- } | {
26
- type: "data-context-substate";
27
- data?: {
28
- key?: ThreadContextSubstateKey | null;
29
- };
30
- transient?: boolean;
31
- } | {
32
- type: "tool-output-available";
33
- toolCallId?: string;
34
- output?: unknown;
35
- } | {
36
- type: "tool-output-error";
37
- toolCallId?: string;
38
- errorText?: string;
39
24
  } | {
40
25
  type: string;
41
26
  [key: string]: unknown;
@@ -57,7 +42,6 @@ export declare function useThread<Context = unknown, Item = Record<string, unkno
57
42
  refresh: () => Promise<void>;
58
43
  setData: import("react").Dispatch<import("react").SetStateAction<ThreadSnapshot<Context, Item> | null>>;
59
44
  contextId: string | null;
60
- substateKey: string | null;
61
45
  applyChunk: (chunk: ThreadStreamChunk) => void;
62
46
  url: string;
63
47
  };
package/dist/react.js CHANGED
@@ -21,7 +21,6 @@ export function useThread(options) {
21
21
  const [isLoading, setIsLoading] = useState(true);
22
22
  const [error, setError] = useState(null);
23
23
  const [contextId, setContextId] = useState(null);
24
- const [substateKey, setSubstateKey] = useState(null);
25
24
  const enabled = options.enabled ?? true;
26
25
  const url = useMemo(() => {
27
26
  if (!enabled || !options.threadKey)
@@ -54,26 +53,15 @@ export function useThread(options) {
54
53
  const applyChunk = useCallback((chunk) => {
55
54
  if (!chunk || typeof chunk !== "object")
56
55
  return;
57
- if (chunk.type === "data-context-id") {
56
+ if (typeof chunk.type === "string" && chunk.type.startsWith("data-context.")) {
58
57
  const payload = "data" in chunk && chunk.data && typeof chunk.data === "object"
59
58
  ? chunk.data
60
59
  : undefined;
61
60
  const candidate = typeof payload?.contextId === "string"
62
61
  ? payload.contextId
63
- : typeof chunk.id === "string"
64
- ? chunk.id
65
- : null;
62
+ : null;
66
63
  if (candidate)
67
64
  setContextId(candidate);
68
- return;
69
- }
70
- if (chunk.type === "data-context-substate") {
71
- const payload = "data" in chunk && chunk.data && typeof chunk.data === "object"
72
- ? chunk.data
73
- : undefined;
74
- const key = payload?.key;
75
- setSubstateKey(typeof key === "string" ? key : null);
76
- return;
77
65
  }
78
66
  }, []);
79
67
  useEffect(() => {
@@ -94,7 +82,6 @@ export function useThread(options) {
94
82
  refresh,
95
83
  setData,
96
84
  contextId,
97
- substateKey,
98
85
  applyChunk,
99
86
  url,
100
87
  };
@@ -0,0 +1,12 @@
1
+ import type { UIMessageChunk } from "ai";
2
+ import type { ChunkEmittedEvent } from "../thread.stream.js";
3
+ export type MapAiSdkChunkToThreadEventParams = {
4
+ chunk: UIMessageChunk | Record<string, unknown>;
5
+ contextId: string;
6
+ executionId?: string;
7
+ stepId?: string;
8
+ itemId?: string;
9
+ provider?: string;
10
+ sequence: number;
11
+ };
12
+ export declare function mapAiSdkChunkToThreadEvent(params: MapAiSdkChunkToThreadEventParams): ChunkEmittedEvent;
@@ -0,0 +1,143 @@
1
+ const REDACT_KEY = /token|authorization|cookie|secret|api[_-]?key|password/i;
2
+ function asRecord(value) {
3
+ if (!value || typeof value !== "object")
4
+ return {};
5
+ return value;
6
+ }
7
+ function readString(record, key) {
8
+ const value = record[key];
9
+ return typeof value === "string" && value.length > 0 ? value : undefined;
10
+ }
11
+ function toJsonSafe(value) {
12
+ if (typeof value === "undefined")
13
+ return undefined;
14
+ try {
15
+ return JSON.parse(JSON.stringify(value));
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ }
21
+ function sanitizeRaw(value) {
22
+ const seen = new WeakSet();
23
+ try {
24
+ return JSON.parse(JSON.stringify(value, (key, currentValue) => {
25
+ if (REDACT_KEY.test(key))
26
+ return "[redacted]";
27
+ if (typeof currentValue === "string" && currentValue.length > 20000) {
28
+ return "[truncated-string]";
29
+ }
30
+ if (currentValue && typeof currentValue === "object") {
31
+ if (seen.has(currentValue))
32
+ return "[circular]";
33
+ seen.add(currentValue);
34
+ }
35
+ return currentValue;
36
+ }));
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ function mapAiSdkChunkType(providerChunkType) {
43
+ switch (providerChunkType) {
44
+ case "start":
45
+ case "stream-start":
46
+ return "chunk.start";
47
+ case "start-step":
48
+ return "chunk.start_step";
49
+ case "finish-step":
50
+ return "chunk.finish_step";
51
+ case "finish":
52
+ return "chunk.finish";
53
+ case "text-start":
54
+ return "chunk.text_start";
55
+ case "text-delta":
56
+ return "chunk.text_delta";
57
+ case "text-end":
58
+ return "chunk.text_end";
59
+ case "reasoning-start":
60
+ return "chunk.reasoning_start";
61
+ case "reasoning-delta":
62
+ return "chunk.reasoning_delta";
63
+ case "reasoning-end":
64
+ return "chunk.reasoning_end";
65
+ case "tool-input-start":
66
+ case "tool-call-start":
67
+ return "chunk.action_input_start";
68
+ case "tool-input-delta":
69
+ case "tool-call-delta":
70
+ return "chunk.action_input_delta";
71
+ case "tool-input-available":
72
+ case "tool-input-end":
73
+ case "tool-call":
74
+ case "tool-call-end":
75
+ return "chunk.action_input_available";
76
+ case "tool-output-available":
77
+ return "chunk.action_output_available";
78
+ case "tool-output-error":
79
+ return "chunk.action_output_error";
80
+ case "source-url":
81
+ return "chunk.source_url";
82
+ case "source-document":
83
+ return "chunk.source_document";
84
+ case "file":
85
+ return "chunk.file";
86
+ case "message-metadata":
87
+ return "chunk.message_metadata";
88
+ case "response-metadata":
89
+ return "chunk.response_metadata";
90
+ case "error":
91
+ return "chunk.error";
92
+ default:
93
+ return "chunk.unknown";
94
+ }
95
+ }
96
+ function buildNormalizedData(chunk) {
97
+ const normalized = {};
98
+ const fields = [
99
+ "id",
100
+ "text",
101
+ "delta",
102
+ "state",
103
+ "toolName",
104
+ "toolCallId",
105
+ "finishReason",
106
+ "url",
107
+ "title",
108
+ "name",
109
+ "mimeType",
110
+ ];
111
+ for (const field of fields) {
112
+ const value = chunk[field];
113
+ if (typeof value !== "undefined") {
114
+ normalized[field] = value;
115
+ }
116
+ }
117
+ if (Object.keys(normalized).length === 0) {
118
+ return {};
119
+ }
120
+ return toJsonSafe(normalized) ?? {};
121
+ }
122
+ export function mapAiSdkChunkToThreadEvent(params) {
123
+ const chunk = asRecord(params.chunk);
124
+ const providerChunkType = readString(chunk, "type") ?? "unknown";
125
+ const chunkType = mapAiSdkChunkType(providerChunkType);
126
+ const actionRef = readString(chunk, "toolCallId") ??
127
+ readString(chunk, "id");
128
+ return {
129
+ type: "chunk.emitted",
130
+ at: new Date().toISOString(),
131
+ chunkType,
132
+ contextId: params.contextId,
133
+ executionId: params.executionId,
134
+ stepId: params.stepId,
135
+ itemId: params.itemId,
136
+ actionRef: chunkType.startsWith("chunk.action_") ? actionRef : undefined,
137
+ provider: params.provider,
138
+ providerChunkType,
139
+ sequence: params.sequence,
140
+ data: buildNormalizedData(chunk),
141
+ raw: sanitizeRaw(chunk),
142
+ };
143
+ }
@@ -57,7 +57,11 @@ export function createAiSdkReactor(options) {
57
57
  });
58
58
  return {
59
59
  assistantEvent: result.assistantEvent,
60
- toolCalls: result.toolCalls,
60
+ actionRequests: result.toolCalls.map((entry) => ({
61
+ actionRef: String(entry.toolCallId),
62
+ actionName: String(entry.toolName),
63
+ input: entry.args,
64
+ })),
61
65
  messagesForModel: result.messagesForModel,
62
66
  llm: result.llm,
63
67
  };
@@ -1,10 +1,10 @@
1
1
  import type { ModelMessage } from "ai";
2
2
  import type { ThreadEnvironment } from "../thread.config.js";
3
3
  import type { ThreadItem } from "../thread.store.js";
4
- import type { ThreadReactionLLM, ThreadReactionToolCall, ThreadReactor, ThreadReactorParams } from "./types.js";
4
+ import type { ThreadReactionLLM, ThreadActionRequest, ThreadReactor, ThreadReactorParams } from "./types.js";
5
5
  type ScriptedReactionPayload = {
6
6
  assistantEvent?: Partial<ThreadItem>;
7
- toolCalls?: ThreadReactionToolCall[];
7
+ actionRequests?: ThreadActionRequest[];
8
8
  messagesForModel?: ModelMessage[];
9
9
  llm?: ThreadReactionLLM;
10
10
  };
@@ -1,7 +1,8 @@
1
+ import { OUTPUT_ITEM_TYPE } from "../thread.events.js";
1
2
  function normalizeScriptedAssistantEvent(params, assistantEvent) {
2
3
  const fallback = {
3
4
  id: params.eventId,
4
- type: "output_text",
5
+ type: OUTPUT_ITEM_TYPE,
5
6
  channel: params.triggerEvent.channel,
6
7
  createdAt: new Date().toISOString(),
7
8
  content: { parts: [] },
@@ -46,7 +47,7 @@ export function createScriptedReactor(options) {
46
47
  : scriptedStep;
47
48
  return {
48
49
  assistantEvent: normalizeScriptedAssistantEvent(params, payload.assistantEvent),
49
- toolCalls: Array.isArray(payload.toolCalls) ? payload.toolCalls : [],
50
+ actionRequests: Array.isArray(payload.actionRequests) ? payload.actionRequests : [],
50
51
  messagesForModel: Array.isArray(payload.messagesForModel)
51
52
  ? payload.messagesForModel
52
53
  : [],
@@ -3,10 +3,10 @@ import type { ThreadEnvironment } from "../thread.config.js";
3
3
  import type { ThreadModelInit } from "../thread.engine.js";
4
4
  import type { ContextIdentifier, StoredContext, ThreadItem } from "../thread.store.js";
5
5
  import type { SerializableToolForModel } from "../tools-to-model-tools.js";
6
- export type ThreadReactionToolCall = {
7
- toolCallId: string;
8
- toolName: string;
9
- args: unknown;
6
+ export type ThreadActionRequest = {
7
+ actionRef: string;
8
+ actionName: string;
9
+ input: unknown;
10
10
  };
11
11
  export type ThreadReactionLLM = {
12
12
  provider?: string;
@@ -22,7 +22,7 @@ export type ThreadReactionLLM = {
22
22
  };
23
23
  export type ThreadReactionResult = {
24
24
  assistantEvent: ThreadItem;
25
- toolCalls: ThreadReactionToolCall[];
25
+ actionRequests: ThreadActionRequest[];
26
26
  messagesForModel: ModelMessage[];
27
27
  llm?: ThreadReactionLLM;
28
28
  };
package/dist/schema.js CHANGED
@@ -8,14 +8,14 @@ export const threadDomain = domain("thread")
8
8
  updatedAt: i.date().optional(),
9
9
  key: i.string().optional().indexed().unique(),
10
10
  name: i.string().optional(),
11
- status: i.string().optional().indexed(), // open | streaming | closed | failed
11
+ status: i.string().optional().indexed(), // idle | streaming
12
12
  }),
13
13
  thread_contexts: i.entity({
14
14
  createdAt: i.date(),
15
15
  updatedAt: i.date().optional(),
16
16
  // Required for prefix/history queries that use `$like` over context keys.
17
17
  key: i.string().optional().indexed().unique(),
18
- status: i.string().optional().indexed(), // open | streaming | closed
18
+ status: i.string().optional().indexed(), // open | closed
19
19
  content: i.any().optional(),
20
20
  }),
21
21
  thread_items: i.entity({
@@ -36,13 +36,14 @@ export const threadDomain = domain("thread")
36
36
  updatedAt: i.date().optional(),
37
37
  status: i.string().optional().indexed(), // running | completed | failed
38
38
  iteration: i.number().indexed(),
39
- // Deterministic ids generated in step runtime; stored for convenience/debugging
40
- executionId: i.string().indexed(),
41
- triggerEventId: i.string().indexed().optional(),
42
- reactionEventId: i.string().indexed().optional(),
43
- eventId: i.string().indexed(),
44
- toolCalls: i.any().optional(),
45
- toolExecutionResults: i.any().optional(),
39
+ // Deterministic ids generated in step runtime; linked to execution/item entities.
40
+ kind: i.string().optional().indexed(), // message | action_execute | action_result
41
+ actionName: i.string().optional().indexed(),
42
+ actionInput: i.any().optional(),
43
+ actionOutput: i.any().optional(),
44
+ actionError: i.string().optional(),
45
+ actionRequests: i.any().optional(),
46
+ actionResults: i.any().optional(),
46
47
  continueLoop: i.boolean().optional(),
47
48
  errorText: i.string().optional(),
48
49
  }),
@@ -7,7 +7,7 @@ import type { ThreadModelInit } from "../thread.engine.js";
7
7
  *
8
8
  * - Performs the provider/network call in a step (Node/runtime allowed).
9
9
  * - Pipes AI SDK UI message chunks into the workflow-owned writable stream.
10
- * - Returns the assistant event + extracted tool calls for the workflow loop.
10
+ * - Returns the assistant event + extracted action requests for the workflow loop.
11
11
  */
12
12
  export declare function doThreadStreamStep(params: {
13
13
  model: ThreadModelInit;
@@ -26,5 +26,9 @@ export declare function doThreadStreamStep(params: {
26
26
  sendStart?: boolean;
27
27
  }): Promise<{
28
28
  assistantEvent: ThreadItem;
29
- toolCalls: import("../thread.toolcalls.js").ToolCall[];
29
+ actionRequests: {
30
+ actionRef: string;
31
+ actionName: string;
32
+ input: any;
33
+ }[];
30
34
  }>;
@@ -1,10 +1,10 @@
1
- import { OUTPUT_TEXT_ITEM_TYPE } from "../thread.events.js";
1
+ import { OUTPUT_ITEM_TYPE } from "../thread.events.js";
2
2
  /**
3
3
  * Runs a single LLM streaming step as a Workflow step.
4
4
  *
5
5
  * - Performs the provider/network call in a step (Node/runtime allowed).
6
6
  * - Pipes AI SDK UI message chunks into the workflow-owned writable stream.
7
- * - Returns the assistant event + extracted tool calls for the workflow loop.
7
+ * - Returns the assistant event + extracted action requests for the workflow loop.
8
8
  */
9
9
  export async function doThreadStreamStep(params) {
10
10
  "use step";
@@ -62,7 +62,7 @@ export async function doThreadStreamStep(params) {
62
62
  const lastMessage = messages[messages.length - 1];
63
63
  const event = {
64
64
  id: params.eventId,
65
- type: OUTPUT_TEXT_ITEM_TYPE,
65
+ type: OUTPUT_ITEM_TYPE,
66
66
  channel: "web",
67
67
  createdAt: new Date().toISOString(),
68
68
  content: { parts: lastMessage?.parts ?? [] },
@@ -84,9 +84,13 @@ export async function doThreadStreamStep(params) {
84
84
  }));
85
85
  await uiStream.pipeTo(writable, { preventClose: true });
86
86
  const assistantEvent = await finishPromise;
87
- const toolCalls = extractToolCallsFromParts(assistantEvent?.content?.parts);
87
+ const actionRequests = extractToolCallsFromParts(assistantEvent?.content?.parts).map((entry) => ({
88
+ actionRef: String(entry.toolCallId),
89
+ actionName: String(entry.toolName),
90
+ input: entry.args,
91
+ }));
88
92
  return {
89
93
  assistantEvent,
90
- toolCalls,
94
+ actionRequests,
91
95
  };
92
96
  }
@@ -1,4 +1,5 @@
1
- import { OUTPUT_TEXT_ITEM_TYPE } from "../thread.events.js";
1
+ import { OUTPUT_ITEM_TYPE } from "../thread.events.js";
2
+ import { mapAiSdkChunkToThreadEvent } from "../reactors/ai-sdk.chunk-map.js";
2
3
  import { writeThreadTraceEvents } from "./trace.steps.js";
3
4
  async function readWorkflowMetadata() {
4
5
  try {
@@ -10,19 +11,17 @@ async function readWorkflowMetadata() {
10
11
  }
11
12
  }
12
13
  async function resolveWorkflowRunId(env, executionId) {
13
- const meta = await readWorkflowMetadata();
14
- let runId = meta && meta.workflowRunId !== undefined && meta.workflowRunId !== null
15
- ? String(meta.workflowRunId)
14
+ const envRunId = env?.workflowRunId;
15
+ let runId = typeof envRunId === "string" && envRunId.trim()
16
+ ? envRunId.trim()
16
17
  : "";
17
- if (!runId) {
18
- const envRunId = env?.workflowRunId;
19
- if (typeof envRunId === "string" && envRunId.trim()) {
20
- runId = envRunId.trim();
21
- }
18
+ const meta = runId ? null : await readWorkflowMetadata();
19
+ if (!runId && meta && meta.workflowRunId !== undefined && meta.workflowRunId !== null) {
20
+ runId = String(meta.workflowRunId);
22
21
  }
23
22
  if (!runId && executionId) {
24
23
  try {
25
- const { getThreadRuntime } = await import("@ekairos/thread/runtime");
24
+ const { getThreadRuntime } = await import("../runtime.js");
26
25
  const runtime = await getThreadRuntime(env);
27
26
  const db = runtime?.db;
28
27
  if (db) {
@@ -82,7 +81,7 @@ function safeErrorJson(error) {
82
81
  */
83
82
  export async function executeReaction(params) {
84
83
  "use step";
85
- const { getThreadRuntime } = await import("@ekairos/thread/runtime");
84
+ const { getThreadRuntime } = await import("../runtime.js");
86
85
  const { store } = await getThreadRuntime(params.env);
87
86
  let events;
88
87
  try {
@@ -132,10 +131,13 @@ export async function executeReaction(params) {
132
131
  result.consumeStream();
133
132
  let resolveFinish;
134
133
  let rejectFinish;
134
+ let chunkSequence = 0;
135
135
  const finishPromise = new Promise((resolve, reject) => {
136
136
  resolveFinish = resolve;
137
137
  rejectFinish = reject;
138
138
  });
139
+ const modelId = typeof params.model === "string" ? params.model : "";
140
+ const mappedProvider = modelId.includes("/") ? modelId.split("/")[0] : undefined;
139
141
  const uiStream = result
140
142
  .toUIMessageStream({
141
143
  sendStart: Boolean(params.sendStart),
@@ -147,7 +149,7 @@ export async function executeReaction(params) {
147
149
  const lastMessage = messages[messages.length - 1];
148
150
  const event = {
149
151
  id: params.eventId,
150
- type: OUTPUT_TEXT_ITEM_TYPE,
152
+ type: OUTPUT_ITEM_TYPE,
151
153
  channel: "web",
152
154
  createdAt: new Date().toISOString(),
153
155
  content: { parts: lastMessage?.parts ?? [] },
@@ -162,6 +164,24 @@ export async function executeReaction(params) {
162
164
  // Filter out per-step finish boundary. Workflow will emit a single finish.
163
165
  .pipeThrough(new TransformStream({
164
166
  transform(chunk, controller) {
167
+ const contextId = typeof params.contextId === "string" && params.contextId.length > 0
168
+ ? params.contextId
169
+ : undefined;
170
+ if (contextId) {
171
+ const mapped = mapAiSdkChunkToThreadEvent({
172
+ chunk,
173
+ contextId,
174
+ executionId: params.executionId,
175
+ stepId: params.stepId,
176
+ itemId: params.eventId,
177
+ provider: mappedProvider,
178
+ sequence: ++chunkSequence,
179
+ });
180
+ controller.enqueue({
181
+ type: "data-chunk.emitted",
182
+ data: mapped,
183
+ });
184
+ }
165
185
  if (chunk.type === "finish")
166
186
  return;
167
187
  controller.enqueue(chunk);
@@ -209,7 +229,6 @@ export async function executeReaction(params) {
209
229
  const usageJson = toPlainJson(usage);
210
230
  const providerMetadataJson = toPlainJson(providerMetadata);
211
231
  // Derive provider/model from gateway id when available.
212
- const modelId = typeof params.model === "string" ? params.model : "";
213
232
  const provider = modelId.includes("/") ? modelId.split("/")[0] : providerMetadata?.provider;
214
233
  const model = modelId.includes("/") ? modelId.split("/").slice(1).join("/") : providerMetadata?.model;
215
234
  // Token accounting: attempt to read cached prompt tokens from OpenAI-like usage shapes.
@@ -1,12 +1,12 @@
1
1
  import type { UIMessageChunk } from "ai";
2
2
  import type { ThreadEnvironment } from "../thread.config.js";
3
- import type { ThreadItem, ContextIdentifier, StoredContext } from "../thread.store.js";
3
+ import type { ThreadItem, ContextIdentifier, StoredContext, ContextStatus } from "../thread.store.js";
4
4
  export type ThreadReviewRequest = {
5
5
  toolCallId: string;
6
6
  toolName?: string;
7
7
  };
8
8
  /**
9
- * Initializes/ensures the story context exists and emits a single `data-context-id` chunk.
9
+ * Initializes/ensures the story context exists.
10
10
  *
11
11
  * This is the "context init" boundary for the story engine.
12
12
  */
@@ -18,13 +18,8 @@ export declare function initializeContext<C>(env: ThreadEnvironment, contextIden
18
18
  isNew: boolean;
19
19
  }>;
20
20
  export declare function updateContextContent<C>(env: ThreadEnvironment, contextIdentifier: ContextIdentifier, content: C): Promise<StoredContext<C>>;
21
- export declare function updateContextStatus(env: ThreadEnvironment, contextIdentifier: ContextIdentifier, status: "open" | "streaming" | "closed"): Promise<void>;
21
+ export declare function updateContextStatus(env: ThreadEnvironment, contextIdentifier: ContextIdentifier, status: ContextStatus): Promise<void>;
22
22
  export declare function saveTriggerItem(env: ThreadEnvironment, contextIdentifier: ContextIdentifier, event: ThreadItem): Promise<ThreadItem>;
23
- export declare function emitContextIdChunk(params: {
24
- env: ThreadEnvironment;
25
- contextId: string;
26
- writable?: WritableStream<UIMessageChunk>;
27
- }): Promise<void>;
28
23
  export declare function saveTriggerAndCreateExecution(params: {
29
24
  env: ThreadEnvironment;
30
25
  contextIdentifier: ContextIdentifier;
@@ -67,7 +62,6 @@ export declare function createThreadStep(params: {
67
62
  iteration: number;
68
63
  }): Promise<{
69
64
  stepId: string;
70
- eventId: string;
71
65
  }>;
72
66
  export declare function updateThreadStep(params: {
73
67
  env: ThreadEnvironment;
@@ -77,8 +71,13 @@ export declare function updateThreadStep(params: {
77
71
  iteration?: number;
78
72
  patch: {
79
73
  status?: "running" | "completed" | "failed";
80
- toolCalls?: any;
81
- toolExecutionResults?: any;
74
+ kind?: "message" | "action_execute" | "action_result";
75
+ actionName?: string;
76
+ actionInput?: unknown;
77
+ actionOutput?: unknown;
78
+ actionError?: string;
79
+ actionRequests?: any;
80
+ actionResults?: any;
82
81
  continueLoop?: boolean;
83
82
  errorText?: string;
84
83
  };