@ekairos/thread 1.22.3 → 1.22.4-beta.feature-thread-unify.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 +292 -353
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/react.d.ts +4 -3
- package/dist/reactors/ai-sdk.reactor.d.ts +38 -0
- package/dist/reactors/ai-sdk.reactor.js +65 -0
- package/dist/reactors/scripted.reactor.d.ts +23 -0
- package/dist/reactors/scripted.reactor.js +56 -0
- package/dist/reactors/types.d.ts +48 -0
- package/dist/reactors/types.js +1 -0
- package/dist/steps/do-thread-stream-step.d.ts +2 -1
- package/dist/steps/do-thread-stream-step.js +4 -2
- package/dist/steps/reaction.steps.d.ts +3 -2
- package/dist/steps/reaction.steps.js +7 -66
- package/dist/steps/store.steps.js +1 -2
- package/dist/steps/trace.steps.d.ts +2 -1
- package/dist/steps/trace.steps.js +9 -4
- package/dist/stores/instant.documents.d.ts +2 -2
- package/dist/stores/instant.documents.js +1 -1
- package/dist/stores/instant.store.js +57 -4
- package/dist/thread.builder.js +4 -1
- package/dist/thread.contract.d.ts +45 -0
- package/dist/thread.contract.js +98 -0
- package/dist/thread.d.ts +1 -1
- package/dist/thread.engine.d.ts +1 -1
- package/dist/thread.engine.js +44 -17
- package/dist/thread.events.js +17 -2
- package/dist/thread.js +1 -1
- package/dist/thread.reactor.d.ts +3 -82
- package/dist/thread.reactor.js +2 -65
- package/dist/thread.store.d.ts +12 -8
- package/dist/thread.stream.d.ts +119 -0
- package/dist/thread.stream.js +152 -0
- package/package.json +9 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
export { thread, createThread, createAiSdkReactor, type CreateAiSdkReactorOptions, 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 ThreadReactionToolCall, 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
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";
|
|
8
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";
|
|
9
11
|
export { useThread, type ThreadSnapshot, type ThreadStreamChunk, type UseThreadOptions, } from "./react.js";
|
|
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";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export {
|
|
2
2
|
// Thread API
|
|
3
|
-
thread, createThread, createAiSdkReactor, Thread, } from "./thread.js";
|
|
3
|
+
thread, createThread, createAiSdkReactor, createScriptedReactor, Thread, } from "./thread.js";
|
|
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
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";
|
|
8
9
|
export { DEFAULT_CODEX_TOOL_NAME, DEFAULT_CODEX_MODEL, codexToolInputSchema, buildDefaultCodexNarrative, didCodexToolExecute, createCodexThreadBuilder, } from "./codex.js";
|
|
9
10
|
export { useThread, } from "./react.js";
|
|
11
|
+
export { parseThreadStreamEvent, assertThreadStreamTransitions, validateThreadStreamTimeline, } from "./thread.stream.js";
|
package/dist/react.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import type { ThreadContextStatus, ThreadContextSubstateKey, ThreadThreadStatus } from "./thread.contract.js";
|
|
1
2
|
export type ThreadSnapshot<Context = unknown, Item = Record<string, unknown>> = {
|
|
2
3
|
thread: {
|
|
3
4
|
id: string;
|
|
4
5
|
key: string;
|
|
5
|
-
status:
|
|
6
|
+
status: ThreadThreadStatus;
|
|
6
7
|
createdAt: string | null;
|
|
7
8
|
updatedAt: string | null;
|
|
8
9
|
};
|
|
9
10
|
context: {
|
|
10
11
|
id: string;
|
|
11
|
-
status:
|
|
12
|
+
status: ThreadContextStatus;
|
|
12
13
|
content: Context;
|
|
13
14
|
createdAt: string | null;
|
|
14
15
|
updatedAt: string | null;
|
|
@@ -24,7 +25,7 @@ export type ThreadStreamChunk = {
|
|
|
24
25
|
} | {
|
|
25
26
|
type: "data-context-substate";
|
|
26
27
|
data?: {
|
|
27
|
-
key?:
|
|
28
|
+
key?: ThreadContextSubstateKey | null;
|
|
28
29
|
};
|
|
29
30
|
transient?: boolean;
|
|
30
31
|
} | {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ThreadEnvironment } from "../thread.config.js";
|
|
2
|
+
import type { ThreadModelInit } from "../thread.engine.js";
|
|
3
|
+
import type { ContextIdentifier, StoredContext, ThreadItem } from "../thread.store.js";
|
|
4
|
+
import type { ThreadReactor } from "./types.js";
|
|
5
|
+
export type CreateAiSdkReactorOptions<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment, Config = unknown> = {
|
|
6
|
+
resolveConfig?: (params: {
|
|
7
|
+
env: Env;
|
|
8
|
+
context: StoredContext<Context>;
|
|
9
|
+
contextIdentifier: ContextIdentifier;
|
|
10
|
+
triggerEvent: ThreadItem;
|
|
11
|
+
model: ThreadModelInit;
|
|
12
|
+
eventId: string;
|
|
13
|
+
executionId: string;
|
|
14
|
+
contextId: string;
|
|
15
|
+
stepId: string;
|
|
16
|
+
iteration: number;
|
|
17
|
+
}) => Promise<Config> | Config;
|
|
18
|
+
selectModel?: (params: {
|
|
19
|
+
env: Env;
|
|
20
|
+
context: StoredContext<Context>;
|
|
21
|
+
triggerEvent: ThreadItem;
|
|
22
|
+
baseModel: ThreadModelInit;
|
|
23
|
+
config: Config;
|
|
24
|
+
}) => Promise<ThreadModelInit> | ThreadModelInit;
|
|
25
|
+
selectMaxModelSteps?: (params: {
|
|
26
|
+
env: Env;
|
|
27
|
+
context: StoredContext<Context>;
|
|
28
|
+
triggerEvent: ThreadItem;
|
|
29
|
+
baseMaxModelSteps: number;
|
|
30
|
+
config: Config;
|
|
31
|
+
}) => Promise<number> | number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Default reactor for Thread: Vercel AI SDK (`streamText`) with tool-call extraction.
|
|
35
|
+
*
|
|
36
|
+
* This keeps current behavior and can be replaced per-thread using `.reactor(...)`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createAiSdkReactor<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment, Config = unknown>(options?: CreateAiSdkReactorOptions<Context, Env, Config>): ThreadReactor<Context, Env>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { executeReaction } from "../steps/reaction.steps.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default reactor for Thread: Vercel AI SDK (`streamText`) with tool-call extraction.
|
|
4
|
+
*
|
|
5
|
+
* This keeps current behavior and can be replaced per-thread using `.reactor(...)`.
|
|
6
|
+
*/
|
|
7
|
+
export function createAiSdkReactor(options) {
|
|
8
|
+
return async (params) => {
|
|
9
|
+
let config;
|
|
10
|
+
if (options?.resolveConfig) {
|
|
11
|
+
config = await options.resolveConfig({
|
|
12
|
+
env: params.env,
|
|
13
|
+
context: params.context,
|
|
14
|
+
contextIdentifier: params.contextIdentifier,
|
|
15
|
+
triggerEvent: params.triggerEvent,
|
|
16
|
+
model: params.model,
|
|
17
|
+
eventId: params.eventId,
|
|
18
|
+
executionId: params.executionId,
|
|
19
|
+
contextId: params.contextId,
|
|
20
|
+
stepId: params.stepId,
|
|
21
|
+
iteration: params.iteration,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const model = options?.selectModel && config !== undefined
|
|
25
|
+
? await options.selectModel({
|
|
26
|
+
env: params.env,
|
|
27
|
+
context: params.context,
|
|
28
|
+
triggerEvent: params.triggerEvent,
|
|
29
|
+
baseModel: params.model,
|
|
30
|
+
config,
|
|
31
|
+
})
|
|
32
|
+
: params.model;
|
|
33
|
+
const maxSteps = options?.selectMaxModelSteps && config !== undefined
|
|
34
|
+
? await options.selectMaxModelSteps({
|
|
35
|
+
env: params.env,
|
|
36
|
+
context: params.context,
|
|
37
|
+
triggerEvent: params.triggerEvent,
|
|
38
|
+
baseMaxModelSteps: params.maxModelSteps,
|
|
39
|
+
config,
|
|
40
|
+
})
|
|
41
|
+
: params.maxModelSteps;
|
|
42
|
+
const result = await executeReaction({
|
|
43
|
+
env: params.env,
|
|
44
|
+
contextIdentifier: params.contextIdentifier,
|
|
45
|
+
model,
|
|
46
|
+
system: params.systemPrompt,
|
|
47
|
+
tools: params.toolsForModel,
|
|
48
|
+
eventId: params.eventId,
|
|
49
|
+
iteration: params.iteration,
|
|
50
|
+
maxSteps,
|
|
51
|
+
sendStart: params.sendStart,
|
|
52
|
+
silent: params.silent,
|
|
53
|
+
writable: params.writable,
|
|
54
|
+
executionId: params.executionId,
|
|
55
|
+
contextId: params.contextId,
|
|
56
|
+
stepId: params.stepId,
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
assistantEvent: result.assistantEvent,
|
|
60
|
+
toolCalls: result.toolCalls,
|
|
61
|
+
messagesForModel: result.messagesForModel,
|
|
62
|
+
llm: result.llm,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ModelMessage } from "ai";
|
|
2
|
+
import type { ThreadEnvironment } from "../thread.config.js";
|
|
3
|
+
import type { ThreadItem } from "../thread.store.js";
|
|
4
|
+
import type { ThreadReactionLLM, ThreadReactionToolCall, ThreadReactor, ThreadReactorParams } from "./types.js";
|
|
5
|
+
type ScriptedReactionPayload = {
|
|
6
|
+
assistantEvent?: Partial<ThreadItem>;
|
|
7
|
+
toolCalls?: ThreadReactionToolCall[];
|
|
8
|
+
messagesForModel?: ModelMessage[];
|
|
9
|
+
llm?: ThreadReactionLLM;
|
|
10
|
+
};
|
|
11
|
+
export type ScriptedReactorStep<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment> = ScriptedReactionPayload | ((params: ThreadReactorParams<Context, Env>) => Promise<ScriptedReactionPayload> | ScriptedReactionPayload);
|
|
12
|
+
export type CreateScriptedReactorOptions<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment> = {
|
|
13
|
+
steps: ScriptedReactorStep<Context, Env>[];
|
|
14
|
+
repeatLast?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Deterministic reactor used for tests and local loop iteration.
|
|
18
|
+
*
|
|
19
|
+
* - No model/network calls
|
|
20
|
+
* - Predictable scripted outputs per iteration
|
|
21
|
+
*/
|
|
22
|
+
export declare function createScriptedReactor<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment>(options: CreateScriptedReactorOptions<Context, Env>): ThreadReactor<Context, Env>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function normalizeScriptedAssistantEvent(params, assistantEvent) {
|
|
2
|
+
const fallback = {
|
|
3
|
+
id: params.eventId,
|
|
4
|
+
type: "output_text",
|
|
5
|
+
channel: params.triggerEvent.channel,
|
|
6
|
+
createdAt: new Date().toISOString(),
|
|
7
|
+
content: { parts: [] },
|
|
8
|
+
};
|
|
9
|
+
const mergedContent = {
|
|
10
|
+
...fallback.content,
|
|
11
|
+
...(assistantEvent?.content ?? {}),
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
...fallback,
|
|
15
|
+
...assistantEvent,
|
|
16
|
+
id: assistantEvent?.id ?? fallback.id,
|
|
17
|
+
createdAt: assistantEvent?.createdAt ?? fallback.createdAt,
|
|
18
|
+
content: mergedContent,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Deterministic reactor used for tests and local loop iteration.
|
|
23
|
+
*
|
|
24
|
+
* - No model/network calls
|
|
25
|
+
* - Predictable scripted outputs per iteration
|
|
26
|
+
*/
|
|
27
|
+
export function createScriptedReactor(options) {
|
|
28
|
+
const steps = Array.isArray(options.steps) ? options.steps : [];
|
|
29
|
+
if (steps.length === 0) {
|
|
30
|
+
throw new Error("createScriptedReactor: options.steps must contain at least one step.");
|
|
31
|
+
}
|
|
32
|
+
let index = 0;
|
|
33
|
+
return async (params) => {
|
|
34
|
+
const hasCurrentStep = index < steps.length;
|
|
35
|
+
if (!hasCurrentStep && !options.repeatLast) {
|
|
36
|
+
throw new Error(`createScriptedReactor: no scripted step available at index ${index}. ` +
|
|
37
|
+
`Provided steps=${steps.length}. Enable repeatLast to reuse the final step.`);
|
|
38
|
+
}
|
|
39
|
+
const stepIndex = hasCurrentStep ? index : steps.length - 1;
|
|
40
|
+
const scriptedStep = steps[stepIndex];
|
|
41
|
+
if (hasCurrentStep) {
|
|
42
|
+
index += 1;
|
|
43
|
+
}
|
|
44
|
+
const payload = typeof scriptedStep === "function"
|
|
45
|
+
? await scriptedStep(params)
|
|
46
|
+
: scriptedStep;
|
|
47
|
+
return {
|
|
48
|
+
assistantEvent: normalizeScriptedAssistantEvent(params, payload.assistantEvent),
|
|
49
|
+
toolCalls: Array.isArray(payload.toolCalls) ? payload.toolCalls : [],
|
|
50
|
+
messagesForModel: Array.isArray(payload.messagesForModel)
|
|
51
|
+
? payload.messagesForModel
|
|
52
|
+
: [],
|
|
53
|
+
llm: payload.llm,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ModelMessage, UIMessageChunk } from "ai";
|
|
2
|
+
import type { ThreadEnvironment } from "../thread.config.js";
|
|
3
|
+
import type { ThreadModelInit } from "../thread.engine.js";
|
|
4
|
+
import type { ContextIdentifier, StoredContext, ThreadItem } from "../thread.store.js";
|
|
5
|
+
import type { SerializableToolForModel } from "../tools-to-model-tools.js";
|
|
6
|
+
export type ThreadReactionToolCall = {
|
|
7
|
+
toolCallId: string;
|
|
8
|
+
toolName: string;
|
|
9
|
+
args: unknown;
|
|
10
|
+
};
|
|
11
|
+
export type ThreadReactionLLM = {
|
|
12
|
+
provider?: string;
|
|
13
|
+
model?: string;
|
|
14
|
+
promptTokens?: number;
|
|
15
|
+
promptTokensCached?: number;
|
|
16
|
+
promptTokensUncached?: number;
|
|
17
|
+
completionTokens?: number;
|
|
18
|
+
totalTokens?: number;
|
|
19
|
+
latencyMs?: number;
|
|
20
|
+
rawUsage?: unknown;
|
|
21
|
+
rawProviderMetadata?: unknown;
|
|
22
|
+
};
|
|
23
|
+
export type ThreadReactionResult = {
|
|
24
|
+
assistantEvent: ThreadItem;
|
|
25
|
+
toolCalls: ThreadReactionToolCall[];
|
|
26
|
+
messagesForModel: ModelMessage[];
|
|
27
|
+
llm?: ThreadReactionLLM;
|
|
28
|
+
};
|
|
29
|
+
export type ThreadReactorParams<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment> = {
|
|
30
|
+
env: Env;
|
|
31
|
+
context: StoredContext<Context>;
|
|
32
|
+
contextIdentifier: ContextIdentifier;
|
|
33
|
+
triggerEvent: ThreadItem;
|
|
34
|
+
model: ThreadModelInit;
|
|
35
|
+
systemPrompt: string;
|
|
36
|
+
actions: Record<string, unknown>;
|
|
37
|
+
toolsForModel: Record<string, SerializableToolForModel>;
|
|
38
|
+
eventId: string;
|
|
39
|
+
executionId: string;
|
|
40
|
+
contextId: string;
|
|
41
|
+
stepId: string;
|
|
42
|
+
iteration: number;
|
|
43
|
+
maxModelSteps: number;
|
|
44
|
+
sendStart: boolean;
|
|
45
|
+
silent: boolean;
|
|
46
|
+
writable: WritableStream<UIMessageChunk>;
|
|
47
|
+
};
|
|
48
|
+
export type ThreadReactor<Context = unknown, Env extends ThreadEnvironment = ThreadEnvironment> = (params: ThreadReactorParams<Context, Env>) => Promise<ThreadReactionResult>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ModelMessage } from "ai";
|
|
2
2
|
import type { ThreadItem } from "../thread.store.js";
|
|
3
3
|
import type { SerializableToolForModel } from "../tools-to-model-tools.js";
|
|
4
|
+
import type { ThreadModelInit } from "../thread.engine.js";
|
|
4
5
|
/**
|
|
5
6
|
* Runs a single LLM streaming step as a Workflow step.
|
|
6
7
|
*
|
|
@@ -9,7 +10,7 @@ import type { SerializableToolForModel } from "../tools-to-model-tools.js";
|
|
|
9
10
|
* - Returns the assistant event + extracted tool calls for the workflow loop.
|
|
10
11
|
*/
|
|
11
12
|
export declare function doThreadStreamStep(params: {
|
|
12
|
-
model:
|
|
13
|
+
model: ThreadModelInit;
|
|
13
14
|
system: string;
|
|
14
15
|
messages: ModelMessage[];
|
|
15
16
|
tools: Record<string, SerializableToolForModel>;
|
|
@@ -11,7 +11,7 @@ export async function doThreadStreamStep(params) {
|
|
|
11
11
|
const { getWritable } = await import("workflow");
|
|
12
12
|
const writable = getWritable();
|
|
13
13
|
const { jsonSchema, gateway, smoothStream, stepCountIs, streamText } = await import("ai");
|
|
14
|
-
const { extractToolCallsFromParts } = await import("
|
|
14
|
+
const { extractToolCallsFromParts } = await import("../thread.toolcalls.js");
|
|
15
15
|
// Match DurableAgent's model init behavior:
|
|
16
16
|
// - string => AI Gateway model id, resolved via `gateway(...)` in the step runtime
|
|
17
17
|
// - function => model factory (should be a `"use step"` function for workflow serialization)
|
|
@@ -19,7 +19,9 @@ export async function doThreadStreamStep(params) {
|
|
|
19
19
|
? gateway(params.model)
|
|
20
20
|
: typeof params.model === "function"
|
|
21
21
|
? await params.model()
|
|
22
|
-
:
|
|
22
|
+
: (() => {
|
|
23
|
+
throw new Error("Invalid model init passed to doThreadStreamStep. Expected a model id string or an async model factory.");
|
|
24
|
+
})();
|
|
23
25
|
// IMPORTANT:
|
|
24
26
|
// `streamText` expects tools in the AI SDK ToolSet shape, where `inputSchema` is a Schema-like value.
|
|
25
27
|
// We pass plain JSON schema objects across the step boundary (serializable), then wrap them here with
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ModelMessage, UIMessageChunk } from "ai";
|
|
2
2
|
import type { ThreadEnvironment } from "../thread.config.js";
|
|
3
|
+
import type { ThreadModelInit } from "../thread.engine.js";
|
|
3
4
|
import type { ThreadItem, ContextIdentifier } from "../thread.store.js";
|
|
4
5
|
import type { SerializableToolForModel } from "../tools-to-model-tools.js";
|
|
5
6
|
/**
|
|
@@ -12,7 +13,7 @@ import type { SerializableToolForModel } from "../tools-to-model-tools.js";
|
|
|
12
13
|
export declare function executeReaction(params: {
|
|
13
14
|
env: ThreadEnvironment;
|
|
14
15
|
contextIdentifier: ContextIdentifier;
|
|
15
|
-
model:
|
|
16
|
+
model: ThreadModelInit;
|
|
16
17
|
system: string;
|
|
17
18
|
tools: Record<string, SerializableToolForModel>;
|
|
18
19
|
eventId: string;
|
|
@@ -20,7 +21,7 @@ export declare function executeReaction(params: {
|
|
|
20
21
|
maxSteps: number;
|
|
21
22
|
sendStart?: boolean;
|
|
22
23
|
silent?: boolean;
|
|
23
|
-
writable
|
|
24
|
+
writable: WritableStream<UIMessageChunk>;
|
|
24
25
|
executionId?: string;
|
|
25
26
|
contextId?: string;
|
|
26
27
|
stepId?: string;
|
|
@@ -100,75 +100,16 @@ export async function executeReaction(params) {
|
|
|
100
100
|
console.error("[ekairos/story] reaction.step store.itemsToModelMessages failed", safeErrorJson(error));
|
|
101
101
|
throw error;
|
|
102
102
|
}
|
|
103
|
-
const writable = params.silent || !params.writable
|
|
104
|
-
? new WritableStream({ write() { } })
|
|
105
|
-
: params.writable;
|
|
106
103
|
const { jsonSchema, gateway, smoothStream, stepCountIs, streamText } = await import("ai");
|
|
107
|
-
const { extractToolCallsFromParts } = await import("
|
|
108
|
-
const isMockModelConfig = (value) => {
|
|
109
|
-
if (!value || typeof value !== "object")
|
|
110
|
-
return false;
|
|
111
|
-
if ("specificationVersion" in value)
|
|
112
|
-
return false;
|
|
113
|
-
if (value.source === "mock")
|
|
114
|
-
return true;
|
|
115
|
-
return typeof value.provider === "string" && typeof value.modelId === "string";
|
|
116
|
-
};
|
|
117
|
-
const buildMockModel = async (config) => {
|
|
118
|
-
const toolName = typeof config.toolName === "string" && config.toolName.trim()
|
|
119
|
-
? config.toolName.trim()
|
|
120
|
-
: Object.keys(params.tools || {})[0] || "tool";
|
|
121
|
-
const provider = config.provider ?? "mock-provider";
|
|
122
|
-
const modelId = config.modelId ?? "mock-model-id";
|
|
123
|
-
return {
|
|
124
|
-
specificationVersion: "v2",
|
|
125
|
-
provider,
|
|
126
|
-
modelId,
|
|
127
|
-
supportedUrls: {},
|
|
128
|
-
doGenerate: async () => ({
|
|
129
|
-
content: [
|
|
130
|
-
{
|
|
131
|
-
type: "tool-call",
|
|
132
|
-
toolCallId: "mock-tool-call",
|
|
133
|
-
toolName,
|
|
134
|
-
input: JSON.stringify({ instruction: "" }),
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
finishReason: "tool-calls",
|
|
138
|
-
usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
|
|
139
|
-
warnings: [],
|
|
140
|
-
}),
|
|
141
|
-
doStream: async () => {
|
|
142
|
-
const toolCallId = `mock-tool-${Date.now()}`;
|
|
143
|
-
const stream = new ReadableStream({
|
|
144
|
-
start(controller) {
|
|
145
|
-
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
146
|
-
controller.enqueue({
|
|
147
|
-
type: "tool-call",
|
|
148
|
-
toolCallId,
|
|
149
|
-
toolName,
|
|
150
|
-
input: JSON.stringify({ instruction: "" }),
|
|
151
|
-
});
|
|
152
|
-
controller.enqueue({
|
|
153
|
-
type: "finish",
|
|
154
|
-
finishReason: "tool-calls",
|
|
155
|
-
usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
|
|
156
|
-
});
|
|
157
|
-
controller.close();
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
return { stream };
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
};
|
|
104
|
+
const { extractToolCallsFromParts } = await import("../thread.toolcalls.js");
|
|
164
105
|
// Match DurableAgent-style model init behavior:
|
|
165
106
|
const resolvedModel = typeof params.model === "string"
|
|
166
107
|
? gateway(params.model)
|
|
167
|
-
:
|
|
168
|
-
? await
|
|
169
|
-
:
|
|
170
|
-
|
|
171
|
-
|
|
108
|
+
: typeof params.model === "function"
|
|
109
|
+
? await params.model()
|
|
110
|
+
: (() => {
|
|
111
|
+
throw new Error("Invalid model init passed to executeReaction. Expected a model id string or an async model factory.");
|
|
112
|
+
})();
|
|
172
113
|
// Wrap plain JSON Schema objects so the AI SDK doesn't attempt Zod conversion at runtime.
|
|
173
114
|
const toolsForStreamText = {};
|
|
174
115
|
for (const [name, t] of Object.entries(params.tools)) {
|
|
@@ -226,7 +167,7 @@ export async function executeReaction(params) {
|
|
|
226
167
|
controller.enqueue(chunk);
|
|
227
168
|
},
|
|
228
169
|
}));
|
|
229
|
-
await uiStream.pipeTo(writable, { preventClose: true });
|
|
170
|
+
await uiStream.pipeTo(params.writable, { preventClose: true });
|
|
230
171
|
const assistantEvent = await finishPromise;
|
|
231
172
|
const finishedAtMs = Date.now();
|
|
232
173
|
const toolCalls = extractToolCallsFromParts(assistantEvent?.content?.parts);
|
|
@@ -75,8 +75,7 @@ function inferDirection(item) {
|
|
|
75
75
|
return undefined;
|
|
76
76
|
}
|
|
77
77
|
function shouldDebugThreadStoreSteps() {
|
|
78
|
-
return
|
|
79
|
-
process.env.PLAYWRIGHT_TEST === "1");
|
|
78
|
+
return process.env.EKAIROS_THREAD_DEBUG === "1";
|
|
80
79
|
}
|
|
81
80
|
function summarizeStepError(error) {
|
|
82
81
|
const err = error;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import "../polyfills/dom-events.js";
|
|
2
2
|
import type { ThreadEnvironment } from "../thread.config.js";
|
|
3
|
+
import type { ThreadTraceEventKind } from "../thread.contract.js";
|
|
3
4
|
export type ThreadTraceEventWrite = {
|
|
4
5
|
workflowRunId: string;
|
|
5
6
|
eventId: string;
|
|
6
|
-
eventKind:
|
|
7
|
+
eventKind: ThreadTraceEventKind;
|
|
7
8
|
seq?: number;
|
|
8
9
|
eventAt?: string;
|
|
9
10
|
contextKey?: string;
|
|
@@ -61,10 +61,15 @@ async function getTraceAuthHeader(baseUrl, projectId) {
|
|
|
61
61
|
return `Bearer ${apiKey}`;
|
|
62
62
|
}
|
|
63
63
|
async function readProjectId() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
try {
|
|
65
|
+
const { getRuntimeProjectId } = await import("@ekairos/domain/runtime");
|
|
66
|
+
const fromConfig = String(getRuntimeProjectId?.() || "").trim();
|
|
67
|
+
if (fromConfig)
|
|
68
|
+
return fromConfig;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// ignore and fall back to env
|
|
72
|
+
}
|
|
68
73
|
const fallback = typeof process !== "undefined" && process.env
|
|
69
74
|
? String(process.env.EKAIROS_PROJECT_ID || "").trim()
|
|
70
75
|
: "";
|
|
@@ -10,7 +10,7 @@ export declare function expandEventsWithInstantDocuments(params: {
|
|
|
10
10
|
*/
|
|
11
11
|
maxChars?: number;
|
|
12
12
|
/**
|
|
13
|
-
* Event type used for derived document text. Defaults to "
|
|
13
|
+
* Event type used for derived document text. Defaults to "output_text".
|
|
14
14
|
*/
|
|
15
|
-
derivedEventType?:
|
|
15
|
+
derivedEventType?: ThreadItem["type"];
|
|
16
16
|
}): Promise<ThreadItem[]>;
|
|
@@ -72,7 +72,7 @@ async function ensureDocumentParsedForFile(db, params) {
|
|
|
72
72
|
export async function expandEventsWithInstantDocuments(params) {
|
|
73
73
|
const db = params.db;
|
|
74
74
|
const maxChars = typeof params.maxChars === "number" ? params.maxChars : 120000;
|
|
75
|
-
const derivedEventType = params.derivedEventType ?? "
|
|
75
|
+
const derivedEventType = params.derivedEventType ?? "output_text";
|
|
76
76
|
const out = [];
|
|
77
77
|
for (const event of params.events) {
|
|
78
78
|
const parts = event?.content?.parts;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "../polyfills/dom-events.js";
|
|
2
2
|
import { id, lookup } from "@instantdb/admin";
|
|
3
3
|
import { convertItemsToModelMessages } from "../thread.events.js";
|
|
4
|
+
import { assertContextTransition, assertExecutionTransition, assertItemTransition, assertStepTransition, assertThreadTransition, } from "../thread.contract.js";
|
|
4
5
|
export { parseAndStoreDocument } from "./instant.document-parser.js";
|
|
5
6
|
import { expandEventsWithInstantDocuments } from "./instant.documents.js";
|
|
6
7
|
export { coerceDocumentTextPages, expandEventsWithInstantDocuments, } from "./instant.documents.js";
|
|
@@ -299,6 +300,9 @@ export class InstantStore {
|
|
|
299
300
|
const thread = await this.getThread(threadIdentifier);
|
|
300
301
|
if (!thread)
|
|
301
302
|
throw new Error("InstantStore: thread not found");
|
|
303
|
+
if (thread.status !== status) {
|
|
304
|
+
assertThreadTransition(thread.status, status);
|
|
305
|
+
}
|
|
302
306
|
await this.db.transact([
|
|
303
307
|
this.db.tx.thread_threads[thread.id].update({
|
|
304
308
|
status,
|
|
@@ -409,6 +413,9 @@ export class InstantStore {
|
|
|
409
413
|
const context = await this.getContext(contextIdentifier);
|
|
410
414
|
if (!context?.id)
|
|
411
415
|
throw new Error("InstantStore: context not found");
|
|
416
|
+
if (context.status !== status) {
|
|
417
|
+
assertContextTransition(context.status, status);
|
|
418
|
+
}
|
|
412
419
|
const txs = [
|
|
413
420
|
this.db.tx.thread_contexts[context.id].update({
|
|
414
421
|
status,
|
|
@@ -416,8 +423,13 @@ export class InstantStore {
|
|
|
416
423
|
}),
|
|
417
424
|
];
|
|
418
425
|
if (context.threadId) {
|
|
426
|
+
const thread = await this.getThread({ id: context.threadId });
|
|
427
|
+
const nextThreadStatus = (status === "closed" ? "closed" : status);
|
|
428
|
+
if (thread && thread.status !== nextThreadStatus) {
|
|
429
|
+
assertThreadTransition(thread.status, nextThreadStatus);
|
|
430
|
+
}
|
|
419
431
|
txs.push(this.db.tx.thread_threads[context.threadId].update({
|
|
420
|
-
status:
|
|
432
|
+
status: nextThreadStatus,
|
|
421
433
|
updatedAt: new Date(),
|
|
422
434
|
}));
|
|
423
435
|
}
|
|
@@ -440,6 +452,10 @@ export class InstantStore {
|
|
|
440
452
|
}
|
|
441
453
|
async saveItem(contextIdentifier, event) {
|
|
442
454
|
const { context, thread } = await this.resolveThreadContext(contextIdentifier);
|
|
455
|
+
const existing = await this.getItem(event.id);
|
|
456
|
+
if (existing?.status && existing.status !== "stored") {
|
|
457
|
+
assertItemTransition(existing.status, "stored");
|
|
458
|
+
}
|
|
443
459
|
const txs = [
|
|
444
460
|
this.db.tx.thread_items[event.id].update({
|
|
445
461
|
...event,
|
|
@@ -474,6 +490,10 @@ export class InstantStore {
|
|
|
474
490
|
return persisted;
|
|
475
491
|
}
|
|
476
492
|
async updateItem(eventId, event) {
|
|
493
|
+
const current = await this.getItem(eventId);
|
|
494
|
+
if (current?.status && event.status && current.status !== event.status) {
|
|
495
|
+
assertItemTransition(current.status, event.status);
|
|
496
|
+
}
|
|
477
497
|
await this.db.transact([this.db.tx.thread_items[eventId].update(event)]);
|
|
478
498
|
const persisted = await this.getItem(eventId);
|
|
479
499
|
if (!persisted)
|
|
@@ -571,12 +591,31 @@ export class InstantStore {
|
|
|
571
591
|
}
|
|
572
592
|
async completeExecution(contextIdentifier, executionId, status) {
|
|
573
593
|
const { context, thread } = await this.resolveThreadContext(contextIdentifier);
|
|
594
|
+
const executionResult = await this.db.query({
|
|
595
|
+
thread_executions: {
|
|
596
|
+
$: { where: { id: executionId }, limit: 1 },
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
const executionRow = executionResult?.thread_executions?.[0];
|
|
600
|
+
if (!executionRow)
|
|
601
|
+
throw new Error("InstantStore: execution not found");
|
|
602
|
+
const currentExecutionStatus = String(executionRow.status ?? "executing");
|
|
603
|
+
if (currentExecutionStatus !== status) {
|
|
604
|
+
assertExecutionTransition(currentExecutionStatus, status);
|
|
605
|
+
}
|
|
606
|
+
if (context.status !== "open") {
|
|
607
|
+
assertContextTransition(context.status, "open");
|
|
608
|
+
}
|
|
609
|
+
const nextThreadStatus = status === "failed" ? "failed" : "open";
|
|
610
|
+
if (thread.status !== nextThreadStatus) {
|
|
611
|
+
assertThreadTransition(thread.status, nextThreadStatus);
|
|
612
|
+
}
|
|
574
613
|
const txs = [];
|
|
575
614
|
txs.push(this.db.tx.thread_executions[executionId].update({ status, updatedAt: new Date() }));
|
|
576
615
|
// Update context status back to "open" when execution completes
|
|
577
616
|
txs.push(this.db.tx.thread_contexts[context.id].update({ status: "open", updatedAt: new Date() }));
|
|
578
617
|
txs.push(this.db.tx.thread_threads[thread.id].update({
|
|
579
|
-
status:
|
|
618
|
+
status: nextThreadStatus,
|
|
580
619
|
updatedAt: new Date(),
|
|
581
620
|
}));
|
|
582
621
|
await this.db.transact(txs);
|
|
@@ -614,6 +653,20 @@ export class InstantStore {
|
|
|
614
653
|
return { id: stepId, eventId };
|
|
615
654
|
}
|
|
616
655
|
async updateStep(stepId, patch) {
|
|
656
|
+
if (patch.status) {
|
|
657
|
+
const stepResult = await this.db.query({
|
|
658
|
+
thread_steps: {
|
|
659
|
+
$: { where: { id: stepId }, limit: 1 },
|
|
660
|
+
},
|
|
661
|
+
});
|
|
662
|
+
const stepRow = stepResult?.thread_steps?.[0];
|
|
663
|
+
if (!stepRow)
|
|
664
|
+
throw new Error("InstantStore: step not found");
|
|
665
|
+
const currentStepStatus = String(stepRow.status ?? "running");
|
|
666
|
+
if (currentStepStatus !== patch.status) {
|
|
667
|
+
assertStepTransition(currentStepStatus, patch.status);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
617
670
|
const update = {
|
|
618
671
|
...patch,
|
|
619
672
|
updatedAt: patch.updatedAt ?? new Date(),
|
|
@@ -708,12 +761,12 @@ export class InstantStore {
|
|
|
708
761
|
}
|
|
709
762
|
}
|
|
710
763
|
// Default behavior for Instant-backed stories:
|
|
711
|
-
// - Expand file parts into derived `
|
|
764
|
+
// - Expand file parts into derived `output_text` events (persisting parsed content into document_documents)
|
|
712
765
|
// - Then convert expanded events to model messages
|
|
713
766
|
const expanded = await expandEventsWithInstantDocuments({
|
|
714
767
|
db: this.db,
|
|
715
768
|
events: eventsWithParts,
|
|
716
|
-
derivedEventType: "
|
|
769
|
+
derivedEventType: "output_text",
|
|
717
770
|
});
|
|
718
771
|
return await convertItemsToModelMessages(expanded);
|
|
719
772
|
}
|