@arcote.tech/arc-chat 0.5.0 → 0.5.2

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-chat",
3
3
  "type": "module",
4
- "version": "0.5.0",
4
+ "version": "0.5.2",
5
5
  "private": false,
6
6
  "description": "Chat module with AI integration for Arc framework",
7
7
  "main": "./src/index.ts",
@@ -10,11 +10,11 @@
10
10
  "type-check": "tsc --noEmit"
11
11
  },
12
12
  "peerDependencies": {
13
- "@arcote.tech/arc": "^0.5.0",
14
- "@arcote.tech/arc-ai": "^0.5.0",
15
- "@arcote.tech/arc-auth": "^0.5.0",
16
- "@arcote.tech/arc-ds": "^0.5.0",
17
- "@arcote.tech/platform": "^0.5.0",
13
+ "@arcote.tech/arc": "^0.5.2",
14
+ "@arcote.tech/arc-ai": "^0.5.2",
15
+ "@arcote.tech/arc-auth": "^0.5.2",
16
+ "@arcote.tech/arc-ds": "^0.5.2",
17
+ "@arcote.tech/platform": "^0.5.2",
18
18
  "lucide-react": ">=0.400.0",
19
19
  "react": ">=18.0.0",
20
20
  "typescript": "^5.0.0"
@@ -1,15 +1,16 @@
1
1
  /// <reference path="../arc.d.ts" />
2
2
  import {
3
3
  aggregate,
4
+ boolean,
4
5
  date,
5
6
  id,
6
7
  string,
7
8
  type ArcId,
8
9
  } from "@arcote.tech/arc";
9
10
  import type { Token } from "@arcote.tech/arc-auth";
10
- import { createStreamSession } from "../streaming/stream-registry";
11
+ // Stream sessions are managed by listeners, not mutations
11
12
 
12
- // ─── ID ──────────────────────────────────────────────────────────
13
+ // ─── ID ──────────────���───────────────────────────────────────────
13
14
 
14
15
  export const createMessageId = <const Name extends string>(data: {
15
16
  name: Name;
@@ -41,11 +42,16 @@ export const createMessageAggregate = <
41
42
  role: string(),
42
43
  content: string(),
43
44
  model: string().optional(),
44
- toolCalls: string().optional(),
45
- toolResults: string().optional(),
45
+ toolName: string().optional(),
46
+ toolCallId: string().optional(),
47
+ sessionId: string().optional(),
48
+ previousResponseId: string().optional(),
49
+ isGenerating: boolean().optional(),
46
50
  usage: string().optional(),
47
51
  createdAt: date(),
48
52
  })
53
+
54
+ // ─── messageSent — user sends a message ─────────────────────
49
55
  .publicEvent(
50
56
  "messageSent",
51
57
  {
@@ -54,7 +60,8 @@ export const createMessageAggregate = <
54
60
  sessionId: string(),
55
61
  role: string(),
56
62
  content: string(),
57
- model: string(),
63
+ model: string().optional(),
64
+ isGenerating: boolean().optional(),
58
65
  },
59
66
  async (ctx, event) => {
60
67
  const p = event.payload;
@@ -63,22 +70,24 @@ export const createMessageAggregate = <
63
70
  role: p.role,
64
71
  content: p.content,
65
72
  model: p.model,
73
+ sessionId: p.sessionId,
74
+ isGenerating: p.isGenerating,
66
75
  createdAt: event.createdAt,
67
76
  });
68
77
  },
69
78
  )
70
79
 
80
+ // ─── assistantResponded — AI generates text ─────────────────
71
81
  .publicEvent(
72
- "assistantMessageCompleted",
82
+ "assistantResponded",
73
83
  {
74
84
  messageId,
75
85
  scopeId,
76
86
  sessionId: string(),
77
87
  content: string(),
78
88
  model: string().optional(),
79
- toolCalls: string().optional(),
80
- toolResults: string().optional(),
81
- usage: string().optional(),
89
+ previousResponseId: string().optional(),
90
+ isGenerating: boolean().optional(),
82
91
  },
83
92
  async (ctx, event) => {
84
93
  const p = event.payload;
@@ -87,28 +96,122 @@ export const createMessageAggregate = <
87
96
  role: "assistant",
88
97
  content: p.content,
89
98
  model: p.model,
90
- toolCalls: p.toolCalls,
91
- toolResults: p.toolResults,
92
- usage: p.usage,
99
+ sessionId: p.sessionId,
100
+ previousResponseId: p.previousResponseId,
101
+ isGenerating: p.isGenerating,
93
102
  createdAt: event.createdAt,
94
103
  });
95
104
  },
96
105
  )
97
106
 
107
+ // ─── toolCalled — AI invokes a tool ─────────────────────────
108
+ .publicEvent(
109
+ "toolCalled",
110
+ {
111
+ messageId,
112
+ scopeId,
113
+ sessionId: string(),
114
+ toolName: string(),
115
+ toolCallId: string(),
116
+ content: string(),
117
+ previousResponseId: string().optional(),
118
+ },
119
+ async (ctx, event) => {
120
+ const p = event.payload;
121
+ await ctx.set(p.messageId, {
122
+ scopeId: p.scopeId,
123
+ role: "tool_call",
124
+ content: p.content,
125
+ toolName: p.toolName,
126
+ toolCallId: p.toolCallId,
127
+ sessionId: p.sessionId,
128
+ previousResponseId: p.previousResponseId,
129
+ createdAt: event.createdAt,
130
+ });
131
+ },
132
+ )
133
+
134
+ // ─── toolExecuted — server tool returns result ──────────────
135
+ .publicEvent(
136
+ "toolExecuted",
137
+ {
138
+ messageId,
139
+ scopeId,
140
+ sessionId: string(),
141
+ toolName: string(),
142
+ toolCallId: string(),
143
+ content: string(),
144
+ isError: boolean().optional(),
145
+ },
146
+ async (ctx, event) => {
147
+ const p = event.payload;
148
+ await ctx.set(p.messageId, {
149
+ scopeId: p.scopeId,
150
+ role: "tool_result",
151
+ content: p.content,
152
+ toolName: p.toolName,
153
+ toolCallId: p.toolCallId,
154
+ sessionId: p.sessionId,
155
+ createdAt: event.createdAt,
156
+ });
157
+ },
158
+ )
159
+
160
+ // ─── userResponded — user answers interactive tool ──────────
161
+ .publicEvent(
162
+ "userResponded",
163
+ {
164
+ messageId,
165
+ scopeId,
166
+ sessionId: string(),
167
+ toolName: string(),
168
+ toolCallId: string(),
169
+ content: string(),
170
+ },
171
+ async (ctx, event) => {
172
+ const p = event.payload;
173
+ await ctx.set(p.messageId, {
174
+ scopeId: p.scopeId,
175
+ role: "tool_result",
176
+ content: p.content,
177
+ toolName: p.toolName,
178
+ toolCallId: p.toolCallId,
179
+ sessionId: p.sessionId,
180
+ createdAt: event.createdAt,
181
+ });
182
+ },
183
+ )
184
+
185
+ // ─── generationCompleted — AI loop finished ────��────────────
186
+ .publicEvent(
187
+ "generationCompleted",
188
+ {
189
+ messageId,
190
+ sessionId: string(),
191
+ usage: string().optional(),
192
+ },
193
+ async (ctx, event) => {
194
+ const p = event.payload;
195
+ await ctx.set(p.messageId, {
196
+ isGenerating: false,
197
+ usage: p.usage,
198
+ } as any);
199
+ },
200
+ )
201
+
202
+ // ─── sendMessage — user sends message, creates session ──────
98
203
  .mutateMethod(
99
204
  "sendMessage",
100
205
  (fn) => fn.withParams({
101
- scopeId,
102
- content: string().minLength(1),
103
- model: string(),
104
- }).handle(
105
- ONLY_SERVER &&
206
+ scopeId,
207
+ content: string().minLength(1),
208
+ model: string(),
209
+ }).handle(
210
+ ONLY_SERVER &&
106
211
  (async (ctx, params) => {
107
212
  const userMsgId = messageId.generate();
108
213
  const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
109
214
 
110
- createStreamSession(sessionId);
111
-
112
215
  await ctx.messageSent.emit({
113
216
  messageId: userMsgId,
114
217
  scopeId: params.scopeId,
@@ -117,42 +220,168 @@ export const createMessageAggregate = <
117
220
  content: params.content,
118
221
  model: params.model,
119
222
  });
120
-
121
- return {
122
- messageId: userMsgId,
123
- sessionId,
124
- };
223
+ return { messageId: userMsgId, sessionId };
125
224
  }),
126
- ))
225
+ ),
226
+ )
127
227
 
228
+ // ─── saveAssistantMessage ─────��─────────────────────────────
128
229
  .mutateMethod(
129
- "completeAssistantMessage",
230
+ "saveAssistantMessage",
130
231
  (fn) => fn.withParams({
131
- scopeId,
132
- sessionId: string(),
133
- content: string(),
134
- model: string().optional(),
135
- toolCalls: string().optional(),
136
- toolResults: string().optional(),
137
- usage: string().optional(),
138
- }).handle(
139
- ONLY_SERVER &&
232
+ scopeId,
233
+ sessionId: string(),
234
+ content: string(),
235
+ model: string().optional(),
236
+ previousResponseId: string().optional(),
237
+ isGenerating: boolean().optional(),
238
+ }).handle(
239
+ ONLY_SERVER &&
140
240
  (async (ctx, params) => {
141
- const assistantMsgId = messageId.generate();
142
- await ctx.assistantMessageCompleted.emit({
143
- messageId: assistantMsgId,
241
+ const msgId = messageId.generate();
242
+ await ctx.assistantResponded.emit({
243
+ messageId: msgId,
144
244
  scopeId: params.scopeId,
145
245
  sessionId: params.sessionId,
146
246
  content: params.content,
147
247
  model: params.model,
148
- toolCalls: params.toolCalls,
149
- toolResults: params.toolResults,
248
+ previousResponseId: params.previousResponseId,
249
+ isGenerating: params.isGenerating,
250
+ });
251
+ return { messageId: msgId };
252
+ }),
253
+ ),
254
+ )
255
+
256
+ // ─── saveToolCall ────────────���──────────────────────────────
257
+ .mutateMethod(
258
+ "saveToolCall",
259
+ (fn) => fn.withParams({
260
+ scopeId,
261
+ sessionId: string(),
262
+ toolName: string(),
263
+ toolCallId: string(),
264
+ content: string(),
265
+ previousResponseId: string().optional(),
266
+ }).handle(
267
+ ONLY_SERVER &&
268
+ (async (ctx, params) => {
269
+ const msgId = messageId.generate();
270
+ await ctx.toolCalled.emit({
271
+ messageId: msgId,
272
+ scopeId: params.scopeId,
273
+ sessionId: params.sessionId,
274
+ toolName: params.toolName,
275
+ toolCallId: params.toolCallId,
276
+ content: params.content,
277
+ previousResponseId: params.previousResponseId,
278
+ });
279
+ return { messageId: msgId };
280
+ }),
281
+ ),
282
+ )
283
+
284
+ // ─── saveToolResult — server tool executed ──────────────────
285
+ .mutateMethod(
286
+ "saveToolResult",
287
+ (fn) => fn.withParams({
288
+ scopeId,
289
+ sessionId: string(),
290
+ toolName: string(),
291
+ toolCallId: string(),
292
+ content: string(),
293
+ isError: boolean().optional(),
294
+ }).handle(
295
+ ONLY_SERVER &&
296
+ (async (ctx, params) => {
297
+ const msgId = messageId.generate();
298
+ await ctx.toolExecuted.emit({
299
+ messageId: msgId,
300
+ scopeId: params.scopeId,
301
+ sessionId: params.sessionId,
302
+ toolName: params.toolName,
303
+ toolCallId: params.toolCallId,
304
+ content: params.content,
305
+ isError: params.isError,
306
+ });
307
+ return { messageId: msgId };
308
+ }),
309
+ ),
310
+ )
311
+
312
+ // ─── completeGeneration ─────────────────────────────────────
313
+ .mutateMethod(
314
+ "completeGeneration",
315
+ (fn) => fn.withParams({
316
+ generationMessageId: messageId,
317
+ sessionId: string(),
318
+ usage: string().optional(),
319
+ }).handle(
320
+ ONLY_SERVER &&
321
+ (async (ctx, params) => {
322
+ await ctx.generationCompleted.emit({
323
+ messageId: params.generationMessageId,
324
+ sessionId: params.sessionId,
150
325
  usage: params.usage,
151
326
  });
152
- return { messageId: assistantMsgId };
327
+ return { ok: true };
328
+ }),
329
+ ),
330
+ )
331
+
332
+ // ─── respondToTool — user answers interactive tool ──────────
333
+ .mutateMethod(
334
+ "respondToTool",
335
+ (fn) => fn.withParams({
336
+ scopeId,
337
+ toolCallId: string(),
338
+ toolName: string(),
339
+ result: string(),
340
+ }).handle(
341
+ ONLY_SERVER &&
342
+ (async (ctx, params) => {
343
+ const msgId = messageId.generate();
344
+ const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
345
+
346
+ await ctx.userResponded.emit({
347
+ messageId: msgId,
348
+ scopeId: params.scopeId,
349
+ sessionId,
350
+ toolName: params.toolName,
351
+ toolCallId: params.toolCallId,
352
+ content: params.result,
353
+ });
354
+ return { messageId: msgId, sessionId };
153
355
  }),
154
- ))
356
+ ),
357
+ )
358
+
359
+ // ─── startStage — initiate stage with greeting ──────────────
360
+ .mutateMethod(
361
+ "startStage",
362
+ (fn) => fn.withParams({
363
+ scopeId,
364
+ model: string().optional(),
365
+ }).handle(
366
+ ONLY_SERVER &&
367
+ (async (ctx, params) => {
368
+ const userMsgId = messageId.generate();
369
+ const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
370
+
371
+ await ctx.messageSent.emit({
372
+ messageId: userMsgId,
373
+ scopeId: params.scopeId,
374
+ sessionId,
375
+ role: "user",
376
+ content: "Rozpocznij ten etap. Przywitaj się i zadaj pierwsze pytanie.",
377
+ model: params.model ?? "gpt-5.4-nano",
378
+ });
379
+ return { messageId: userMsgId, sessionId };
380
+ }),
381
+ ),
382
+ )
155
383
 
384
+ // ─── getByScope ─────────────────────────────────────────────
156
385
  .clientQuery(
157
386
  "getByScope",
158
387
  (fn) => fn
@@ -162,7 +391,7 @@ export const createMessageAggregate = <
162
391
  ),
163
392
  )
164
393
 
165
- .protectBy(userToken, (p) => ({ accountId: p.accountId }));
394
+ .protectBy(userToken, (p: { workspaceId: string }) => ({ scopeId: p.workspaceId }));
166
395
  };
167
396
 
168
397
  export type MessageAggregate = ReturnType<typeof createMessageAggregate>;