@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.
@@ -0,0 +1,119 @@
1
+ import { type ThreadContextStatus, type ThreadExecutionStatus, type ThreadItemStatus, type ThreadStepStatus, type ThreadStreamChunkType, type ThreadThreadStatus } from "./thread.contract.js";
2
+ type IsoDateString = string;
3
+ type ThreadStreamEventBase = {
4
+ type: string;
5
+ at: IsoDateString;
6
+ };
7
+ export type ContextCreatedEvent = ThreadStreamEventBase & {
8
+ type: "context.created";
9
+ contextId: string;
10
+ threadId: string;
11
+ status: ThreadContextStatus;
12
+ };
13
+ export type ContextResolvedEvent = ThreadStreamEventBase & {
14
+ type: "context.resolved";
15
+ contextId: string;
16
+ threadId: string;
17
+ status: ThreadContextStatus;
18
+ };
19
+ export type ContextStatusChangedEvent = ThreadStreamEventBase & {
20
+ type: "context.status.changed";
21
+ contextId: string;
22
+ threadId: string;
23
+ from: ThreadContextStatus;
24
+ to: ThreadContextStatus;
25
+ };
26
+ export type ThreadCreatedEvent = ThreadStreamEventBase & {
27
+ type: "thread.created";
28
+ threadId: string;
29
+ status: ThreadThreadStatus;
30
+ };
31
+ export type ThreadResolvedEvent = ThreadStreamEventBase & {
32
+ type: "thread.resolved";
33
+ threadId: string;
34
+ status: ThreadThreadStatus;
35
+ };
36
+ export type ThreadStatusChangedEvent = ThreadStreamEventBase & {
37
+ type: "thread.status.changed";
38
+ threadId: string;
39
+ from: ThreadThreadStatus;
40
+ to: ThreadThreadStatus;
41
+ };
42
+ export type ExecutionCreatedEvent = ThreadStreamEventBase & {
43
+ type: "execution.created";
44
+ executionId: string;
45
+ contextId: string;
46
+ threadId: string;
47
+ status: ThreadExecutionStatus;
48
+ };
49
+ export type ExecutionStatusChangedEvent = ThreadStreamEventBase & {
50
+ type: "execution.status.changed";
51
+ executionId: string;
52
+ contextId: string;
53
+ threadId: string;
54
+ from: ThreadExecutionStatus;
55
+ to: ThreadExecutionStatus;
56
+ };
57
+ export type ThreadFinishedEvent = ThreadStreamEventBase & {
58
+ type: "thread.finished";
59
+ threadId: string;
60
+ contextId: string;
61
+ executionId: string;
62
+ result: "completed" | "failed";
63
+ };
64
+ export type ItemCreatedEvent = ThreadStreamEventBase & {
65
+ type: "item.created";
66
+ itemId: string;
67
+ contextId: string;
68
+ threadId: string;
69
+ executionId?: string;
70
+ status: ThreadItemStatus;
71
+ };
72
+ export type ItemStatusChangedEvent = ThreadStreamEventBase & {
73
+ type: "item.status.changed";
74
+ itemId: string;
75
+ executionId?: string;
76
+ from: ThreadItemStatus;
77
+ to: ThreadItemStatus;
78
+ };
79
+ export type StepCreatedEvent = ThreadStreamEventBase & {
80
+ type: "step.created";
81
+ stepId: string;
82
+ executionId: string;
83
+ iteration: number;
84
+ status: ThreadStepStatus;
85
+ };
86
+ export type StepStatusChangedEvent = ThreadStreamEventBase & {
87
+ type: "step.status.changed";
88
+ stepId: string;
89
+ executionId: string;
90
+ from: ThreadStepStatus;
91
+ to: ThreadStepStatus;
92
+ };
93
+ export type PartCreatedEvent = ThreadStreamEventBase & {
94
+ type: "part.created";
95
+ partKey: string;
96
+ stepId: string;
97
+ idx: number;
98
+ part?: unknown;
99
+ };
100
+ export type PartUpdatedEvent = ThreadStreamEventBase & {
101
+ type: "part.updated";
102
+ partKey: string;
103
+ stepId: string;
104
+ idx: number;
105
+ part?: unknown;
106
+ };
107
+ export type ChunkEmittedEvent = ThreadStreamEventBase & {
108
+ type: "chunk.emitted";
109
+ chunkType: ThreadStreamChunkType;
110
+ contextId: string;
111
+ executionId?: string;
112
+ stepId?: string;
113
+ data?: unknown;
114
+ };
115
+ export type ThreadStreamEvent = ContextCreatedEvent | ContextResolvedEvent | ContextStatusChangedEvent | ThreadCreatedEvent | ThreadResolvedEvent | ThreadStatusChangedEvent | ExecutionCreatedEvent | ExecutionStatusChangedEvent | ThreadFinishedEvent | ItemCreatedEvent | ItemStatusChangedEvent | StepCreatedEvent | StepStatusChangedEvent | PartCreatedEvent | PartUpdatedEvent | ChunkEmittedEvent;
116
+ export declare function parseThreadStreamEvent(value: unknown): ThreadStreamEvent;
117
+ export declare function assertThreadStreamTransitions(event: ThreadStreamEvent): void;
118
+ export declare function validateThreadStreamTimeline(events: readonly ThreadStreamEvent[]): void;
119
+ export {};
@@ -0,0 +1,152 @@
1
+ import { assertContextTransition, assertExecutionTransition, assertItemTransition, assertStepTransition, assertThreadTransition, } from "./thread.contract.js";
2
+ function assertObject(value, label) {
3
+ if (!value || typeof value !== "object") {
4
+ throw new Error(`Invalid ${label}: expected object.`);
5
+ }
6
+ }
7
+ function assertString(value, label) {
8
+ if (typeof value !== "string" || value.length === 0) {
9
+ throw new Error(`Invalid ${label}: expected non-empty string.`);
10
+ }
11
+ }
12
+ function assertNumber(value, label) {
13
+ if (typeof value !== "number" || Number.isNaN(value)) {
14
+ throw new Error(`Invalid ${label}: expected number.`);
15
+ }
16
+ }
17
+ export function parseThreadStreamEvent(value) {
18
+ assertObject(value, "thread stream event");
19
+ assertString(value.type, "thread stream event.type");
20
+ assertString(value.at, "thread stream event.at");
21
+ const type = value.type;
22
+ switch (type) {
23
+ case "context.created":
24
+ case "context.resolved": {
25
+ assertString(value.contextId, `${type}.contextId`);
26
+ assertString(value.threadId, `${type}.threadId`);
27
+ assertString(value.status, `${type}.status`);
28
+ return value;
29
+ }
30
+ case "context.status.changed": {
31
+ assertString(value.contextId, `${type}.contextId`);
32
+ assertString(value.threadId, `${type}.threadId`);
33
+ assertString(value.from, `${type}.from`);
34
+ assertString(value.to, `${type}.to`);
35
+ return value;
36
+ }
37
+ case "thread.created":
38
+ case "thread.resolved": {
39
+ assertString(value.threadId, `${type}.threadId`);
40
+ assertString(value.status, `${type}.status`);
41
+ return value;
42
+ }
43
+ case "thread.status.changed": {
44
+ assertString(value.threadId, `${type}.threadId`);
45
+ assertString(value.from, `${type}.from`);
46
+ assertString(value.to, `${type}.to`);
47
+ return value;
48
+ }
49
+ case "execution.created": {
50
+ assertString(value.executionId, `${type}.executionId`);
51
+ assertString(value.contextId, `${type}.contextId`);
52
+ assertString(value.threadId, `${type}.threadId`);
53
+ assertString(value.status, `${type}.status`);
54
+ return value;
55
+ }
56
+ case "execution.status.changed": {
57
+ assertString(value.executionId, `${type}.executionId`);
58
+ assertString(value.contextId, `${type}.contextId`);
59
+ assertString(value.threadId, `${type}.threadId`);
60
+ assertString(value.from, `${type}.from`);
61
+ assertString(value.to, `${type}.to`);
62
+ return value;
63
+ }
64
+ case "thread.finished": {
65
+ assertString(value.threadId, `${type}.threadId`);
66
+ assertString(value.contextId, `${type}.contextId`);
67
+ assertString(value.executionId, `${type}.executionId`);
68
+ assertString(value.result, `${type}.result`);
69
+ return value;
70
+ }
71
+ case "item.created": {
72
+ assertString(value.itemId, `${type}.itemId`);
73
+ assertString(value.contextId, `${type}.contextId`);
74
+ assertString(value.threadId, `${type}.threadId`);
75
+ assertString(value.status, `${type}.status`);
76
+ if (value.executionId !== undefined) {
77
+ assertString(value.executionId, `${type}.executionId`);
78
+ }
79
+ return value;
80
+ }
81
+ case "item.status.changed": {
82
+ assertString(value.itemId, `${type}.itemId`);
83
+ assertString(value.from, `${type}.from`);
84
+ assertString(value.to, `${type}.to`);
85
+ if (value.executionId !== undefined) {
86
+ assertString(value.executionId, `${type}.executionId`);
87
+ }
88
+ return value;
89
+ }
90
+ case "step.created": {
91
+ assertString(value.stepId, `${type}.stepId`);
92
+ assertString(value.executionId, `${type}.executionId`);
93
+ assertNumber(value.iteration, `${type}.iteration`);
94
+ assertString(value.status, `${type}.status`);
95
+ return value;
96
+ }
97
+ case "step.status.changed": {
98
+ assertString(value.stepId, `${type}.stepId`);
99
+ assertString(value.executionId, `${type}.executionId`);
100
+ assertString(value.from, `${type}.from`);
101
+ assertString(value.to, `${type}.to`);
102
+ return value;
103
+ }
104
+ case "part.created":
105
+ case "part.updated": {
106
+ assertString(value.partKey, `${type}.partKey`);
107
+ assertString(value.stepId, `${type}.stepId`);
108
+ assertNumber(value.idx, `${type}.idx`);
109
+ return value;
110
+ }
111
+ case "chunk.emitted": {
112
+ assertString(value.chunkType, `${type}.chunkType`);
113
+ assertString(value.contextId, `${type}.contextId`);
114
+ if (value.executionId !== undefined) {
115
+ assertString(value.executionId, `${type}.executionId`);
116
+ }
117
+ if (value.stepId !== undefined) {
118
+ assertString(value.stepId, `${type}.stepId`);
119
+ }
120
+ return value;
121
+ }
122
+ default: {
123
+ throw new Error(`Unsupported thread stream event type: ${type}`);
124
+ }
125
+ }
126
+ }
127
+ export function assertThreadStreamTransitions(event) {
128
+ switch (event.type) {
129
+ case "context.status.changed":
130
+ assertContextTransition(event.from, event.to);
131
+ return;
132
+ case "thread.status.changed":
133
+ assertThreadTransition(event.from, event.to);
134
+ return;
135
+ case "execution.status.changed":
136
+ assertExecutionTransition(event.from, event.to);
137
+ return;
138
+ case "item.status.changed":
139
+ assertItemTransition(event.from, event.to);
140
+ return;
141
+ case "step.status.changed":
142
+ assertStepTransition(event.from, event.to);
143
+ return;
144
+ default:
145
+ return;
146
+ }
147
+ }
148
+ export function validateThreadStreamTimeline(events) {
149
+ for (const event of events) {
150
+ assertThreadStreamTransitions(event);
151
+ }
152
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekairos/thread",
3
- "version": "1.22.3",
3
+ "version": "1.22.4-beta.feature-thread-unify.0",
4
4
  "description": "Pulzar Thread - Workflow-based AI Threads",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -106,11 +106,14 @@
106
106
  "dev": "tsc -p tsconfig.json --watch",
107
107
  "watch": "tsc -p tsconfig.json --watch",
108
108
  "clean": "node -e \"require('fs').rmSync('dist', {recursive:true, force:true})\"",
109
- "typecheck": "tsc --noEmit"
109
+ "typecheck": "tsc --noEmit",
110
+ "test": "vitest run -c vitest.config.mts",
111
+ "test:ai-sdk-reactor": "vitest run -c vitest.config.mts src/tests/thread.ai-sdk-reactor.instant.test.ts",
112
+ "test:scripted-reactor": "vitest run -c vitest.config.mts src/tests/thread.scripted-reactor.instant.test.ts"
110
113
  },
111
114
  "dependencies": {
112
115
  "@ai-sdk/openai": "^2.0.52",
113
- "@ekairos/domain": "^1.22.3",
116
+ "@ekairos/domain": "^1.22.4-beta.feature-thread-unify.0",
114
117
  "@instantdb/admin": "0.22.126",
115
118
  "@instantdb/core": "0.22.126",
116
119
  "@vercel/mcp-adapter": "^1.0.0",
@@ -125,9 +128,12 @@
125
128
  "zod": "^4.1.8"
126
129
  },
127
130
  "devDependencies": {
131
+ "@ekairos/testing": "workspace:*",
128
132
  "@ekairos/tsconfig": "workspace:*",
129
133
  "@types/node": "^24.5.0",
130
134
  "@types/react": "^19.2.2",
135
+ "dotenv": "^17.2.3",
136
+ "vitest": "^3.2.4",
131
137
  "typescript": "^5.9.2"
132
138
  }
133
139
  }