@percena/weft-react 0.1.5-next.0 → 0.1.5-next.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/README.md CHANGED
@@ -19,6 +19,6 @@ function Support({ boot }: { boot: { server: string; token: string; sessionId: s
19
19
  ```
20
20
 
21
21
  `{ server, token, sessionId }` comes from your backend's embed bootstrap
22
- (`@percena/weft-node` `weft.sessions.create(...)`). Re-exports the full
22
+ (`POST /v1/embed/sessions` via direct `fetch()`). Re-exports the full
23
23
  `@weft/ui` + `@weft/local-chat` surface (including `EN_FALLBACK` and
24
24
  `createFlitroEmbedRuntime`). Peers: `react`, `react-dom`.
package/dist/index.cjs CHANGED
@@ -4177,8 +4177,19 @@ var TurnCard = React10.memo(function TurnCard2({
4177
4177
  if (prev.isLastResponse !== next.isLastResponse) return false;
4178
4178
  if (prev.displayMode !== next.displayMode) return false;
4179
4179
  if (prev.annotationInteractionMode !== next.annotationInteractionMode) return false;
4180
- if (prev.activities !== next.activities) return false;
4181
- if (prev.response !== next.response) return false;
4180
+ if (prev.activities !== next.activities) {
4181
+ if (prev.activities.length !== next.activities.length) return false;
4182
+ for (let i = 0; i < prev.activities.length; i++) {
4183
+ const pa = prev.activities[i], na = next.activities[i];
4184
+ if (pa.id !== na.id || pa.status !== na.status || pa.content !== na.content) return false;
4185
+ }
4186
+ }
4187
+ if (prev.response !== next.response) {
4188
+ if (!prev.response !== !next.response) return false;
4189
+ if (prev.response && next.response) {
4190
+ if (prev.response.text !== next.response.text || prev.response.isStreaming !== next.response.isStreaming || prev.response.messageId !== next.response.messageId || prev.response.annotations?.length !== next.response.annotations?.length) return false;
4191
+ }
4192
+ }
4182
4193
  if (prev.openAnnotationRequest !== next.openAnnotationRequest) return false;
4183
4194
  if (prev.hasActiveFollowUpAnnotations !== next.hasActiveFollowUpAnnotations) return false;
4184
4195
  return prev.sessionId === next.sessionId && prev.turnId === next.turnId;
@@ -5438,6 +5449,24 @@ function handleTextDelta(state, event) {
5438
5449
  const streamingIndex = findStreamingMessage(session.messages, event.turnId);
5439
5450
  if (streamingIndex !== -1) {
5440
5451
  const currentMsg = session.messages[streamingIndex];
5452
+ if (currentMsg.isIntermediate && !event.isIntermediate) {
5453
+ const closedSession = updateMessageAt(session, streamingIndex, {
5454
+ isStreaming: false
5455
+ });
5456
+ const newMessage2 = {
5457
+ id: generateMessageId2(),
5458
+ role: "assistant",
5459
+ content: event.delta,
5460
+ timestamp: event.timestamp ?? Date.now(),
5461
+ isStreaming: true,
5462
+ isPending: true,
5463
+ turnId: event.turnId
5464
+ };
5465
+ return {
5466
+ session: appendMessage(closedSession, newMessage2, false),
5467
+ streaming: { content: event.delta, turnId: event.turnId }
5468
+ };
5469
+ }
5441
5470
  const updatedSession = updateMessageAt(session, streamingIndex, {
5442
5471
  content: currentMsg.content + event.delta
5443
5472
  });
@@ -5450,7 +5479,8 @@ function handleTextDelta(state, event) {
5450
5479
  timestamp: event.timestamp ?? Date.now(),
5451
5480
  isStreaming: true,
5452
5481
  isPending: true,
5453
- turnId: event.turnId
5482
+ turnId: event.turnId,
5483
+ ...event.isIntermediate ? { isIntermediate: true } : {}
5454
5484
  };
5455
5485
  return {
5456
5486
  session: appendMessage(session, newMessage, false),
@@ -5460,12 +5490,15 @@ function handleTextDelta(state, event) {
5460
5490
  function handleTextComplete(state, event) {
5461
5491
  const { session, streaming } = state;
5462
5492
  let msgIndex = findStreamingMessage(session.messages, event.turnId);
5493
+ if (msgIndex !== -1 && event.isIntermediate && !session.messages[msgIndex].isIntermediate) {
5494
+ msgIndex = -1;
5495
+ }
5463
5496
  if (msgIndex === -1) {
5464
5497
  msgIndex = findAssistantMessage(session.messages, event.turnId);
5465
5498
  }
5466
5499
  if (msgIndex !== -1) {
5467
5500
  const existingMsg = session.messages[msgIndex];
5468
- if (!existingMsg.isStreaming && existingMsg.isIntermediate && event.isIntermediate) {
5501
+ if (!existingMsg.isStreaming && existingMsg.isIntermediate && !existingMsg.isPending) {
5469
5502
  msgIndex = -1;
5470
5503
  }
5471
5504
  }
@@ -6533,14 +6566,16 @@ function mapCoreEventToProcessorEvent(coreEvent, sessionId) {
6533
6566
  type: "text_delta",
6534
6567
  sessionId,
6535
6568
  delta: coreEvent.text,
6536
- turnId: coreEvent.turnId
6569
+ turnId: coreEvent.turnId,
6570
+ isIntermediate: true
6537
6571
  };
6538
6572
  case "reasoning":
6539
6573
  return {
6540
6574
  type: "text_complete",
6541
6575
  sessionId,
6542
6576
  text: coreEvent.text,
6543
- turnId: coreEvent.turnId
6577
+ turnId: coreEvent.turnId,
6578
+ isIntermediate: true
6544
6579
  };
6545
6580
  case "permission_response":
6546
6581
  return {
@@ -6580,7 +6615,8 @@ function mapTimelineEnvelopeToProcessorEvent(envelope) {
6580
6615
  sessionId,
6581
6616
  delta: item.text,
6582
6617
  turnId: item.turnId,
6583
- timestamp
6618
+ timestamp,
6619
+ ...item.type === "reasoning_delta" && { isIntermediate: true }
6584
6620
  };
6585
6621
  case "assistant_message":
6586
6622
  case "reasoning":
@@ -6590,7 +6626,8 @@ function mapTimelineEnvelopeToProcessorEvent(envelope) {
6590
6626
  text: item.text,
6591
6627
  turnId: item.turnId,
6592
6628
  timestamp,
6593
- messageId: item.messageId
6629
+ messageId: item.messageId,
6630
+ ...item.type === "reasoning" && { isIntermediate: true }
6594
6631
  };
6595
6632
  case "tool_call":
6596
6633
  return {
@@ -7266,6 +7303,17 @@ function useAgentChatSession(options) {
7266
7303
  function useTimelineAgentChatSession(options) {
7267
7304
  const { runtime, workspaceId = "local-workspace", workspaceName = "Local Workspace" } = options;
7268
7305
  const [timeline, setTimeline] = (0, import_react19.useState)([]);
7306
+ const [sessionState, setSessionState] = (0, import_react19.useState)(() => ({
7307
+ session: {
7308
+ id: runtime.sessionId,
7309
+ workspaceId,
7310
+ workspaceName,
7311
+ lastMessageAt: 0,
7312
+ messages: [],
7313
+ isProcessing: false
7314
+ },
7315
+ streaming: null
7316
+ }));
7269
7317
  const [capabilityReport, setCapabilityReport] = (0, import_react19.useState)(null);
7270
7318
  const [error, setError] = (0, import_react19.useState)(null);
7271
7319
  const [isReconnecting, setIsReconnecting] = (0, import_react19.useState)(false);
@@ -7276,6 +7324,11 @@ function useTimelineAgentChatSession(options) {
7276
7324
  const onEvent = (envelope) => {
7277
7325
  lastCursorRef.current = { epoch: envelope.epoch, afterSeq: envelope.seq };
7278
7326
  setTimeline((current) => mergeTimeline(current, [envelope]));
7327
+ if (isChatTranscriptTimelineEnvelope(envelope)) {
7328
+ setSessionState(
7329
+ (current) => processEvent(current, mapTimelineEnvelopeToProcessorEvent(envelope)).state
7330
+ );
7331
+ }
7279
7332
  setIsReconnecting(false);
7280
7333
  };
7281
7334
  const onError = (err) => {
@@ -7290,6 +7343,16 @@ function useTimelineAgentChatSession(options) {
7290
7343
  if (catchupAbort) return;
7291
7344
  if (result.items.length > 0) {
7292
7345
  setTimeline((current) => mergeTimeline(current, result.items));
7346
+ const chatEvents = result.items.filter(isChatTranscriptTimelineEnvelope).sort((a, b) => a.epoch !== b.epoch ? a.epoch.localeCompare(b.epoch) : a.seq - b.seq);
7347
+ if (chatEvents.length > 0) {
7348
+ setSessionState((current) => {
7349
+ let s = current;
7350
+ for (const env of chatEvents) {
7351
+ s = processEvent(s, mapTimelineEnvelopeToProcessorEvent(env)).state;
7352
+ }
7353
+ return s;
7354
+ });
7355
+ }
7293
7356
  }
7294
7357
  if (result.hasGap) {
7295
7358
  setHasGap(true);
@@ -7328,25 +7391,23 @@ function useTimelineAgentChatSession(options) {
7328
7391
  const respondToPermission = (0, import_react19.useCallback)(async (requestId, allowed, remember) => {
7329
7392
  await runtime.commands.respondToPermission(requestId, allowed, remember);
7330
7393
  }, [runtime]);
7331
- const model = (0, import_react19.useMemo)(
7332
- () => createTimelineAgentChatPanelModel({
7333
- timeline,
7334
- sessionId: runtime.sessionId,
7335
- workspaceId,
7336
- workspaceName,
7337
- runtimeState: runtime.getState(),
7338
- capabilityReport,
7339
- error
7340
- }),
7341
- [timeline, runtime, workspaceId, workspaceName, capabilityReport, error]
7394
+ const turns = (0, import_react19.useMemo)(
7395
+ () => groupMessagesByTurn(sessionState.session.messages),
7396
+ [sessionState]
7342
7397
  );
7398
+ const runtimeState = runtime.getState();
7399
+ const isRunning = runtimeState.status === "running" || runtimeState.status === "preflighting" || runtimeState.status === "starting" || runtimeState.status === "waiting_for_permission";
7343
7400
  const isConnected = runtime.events.isConnected();
7344
7401
  return {
7345
- ...model,
7402
+ session: sessionState.session,
7403
+ turns,
7346
7404
  timeline,
7405
+ isRunning,
7347
7406
  isConnected,
7348
7407
  isReconnecting,
7349
7408
  hasGap,
7409
+ capabilityReport,
7410
+ error,
7350
7411
  sendMessage,
7351
7412
  abort,
7352
7413
  respondToPermission
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ export { CreateFlitroEmbedRuntimeOptions, createFlitroEmbedRuntime };
5
5
  * useAgentSession — the official L1 runtime-configuration API.
6
6
  *
7
7
  * Wraps `createFlitroEmbedRuntime` in a hook: the host backend mints
8
- * `{ server, token, sessionId }` (e.g. with `@percena/weft-node`) and the
8
+ * `{ server, token, sessionId }` via `POST /v1/embed/sessions` and the
9
9
  * component renders `<TimelineAgentChatPanel runtime={session.runtime} />`.
10
10
  */
11
11
 
@@ -3261,6 +3261,8 @@ interface TextDeltaEvent {
3261
3261
  turnId?: string;
3262
3262
  /** Timestamp from canonical timeline for stable ordering */
3263
3263
  timestamp?: number;
3264
+ /** When true, this delta belongs to a reasoning/thinking block (intermediate content). */
3265
+ isIntermediate?: boolean;
3264
3266
  }
3265
3267
  /**
3266
3268
  * Text complete event - finalizes streaming text
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { CreateFlitroEmbedRuntimeOptions, createFlitroEmbedRuntime };
5
5
  * useAgentSession — the official L1 runtime-configuration API.
6
6
  *
7
7
  * Wraps `createFlitroEmbedRuntime` in a hook: the host backend mints
8
- * `{ server, token, sessionId }` (e.g. with `@percena/weft-node`) and the
8
+ * `{ server, token, sessionId }` via `POST /v1/embed/sessions` and the
9
9
  * component renders `<TimelineAgentChatPanel runtime={session.runtime} />`.
10
10
  */
11
11
 
@@ -3261,6 +3261,8 @@ interface TextDeltaEvent {
3261
3261
  turnId?: string;
3262
3262
  /** Timestamp from canonical timeline for stable ordering */
3263
3263
  timestamp?: number;
3264
+ /** When true, this delta belongs to a reasoning/thinking block (intermediate content). */
3265
+ isIntermediate?: boolean;
3264
3266
  }
3265
3267
  /**
3266
3268
  * Text complete event - finalizes streaming text
package/dist/index.js CHANGED
@@ -4100,8 +4100,19 @@ var TurnCard = React10.memo(function TurnCard2({
4100
4100
  if (prev.isLastResponse !== next.isLastResponse) return false;
4101
4101
  if (prev.displayMode !== next.displayMode) return false;
4102
4102
  if (prev.annotationInteractionMode !== next.annotationInteractionMode) return false;
4103
- if (prev.activities !== next.activities) return false;
4104
- if (prev.response !== next.response) return false;
4103
+ if (prev.activities !== next.activities) {
4104
+ if (prev.activities.length !== next.activities.length) return false;
4105
+ for (let i = 0; i < prev.activities.length; i++) {
4106
+ const pa = prev.activities[i], na = next.activities[i];
4107
+ if (pa.id !== na.id || pa.status !== na.status || pa.content !== na.content) return false;
4108
+ }
4109
+ }
4110
+ if (prev.response !== next.response) {
4111
+ if (!prev.response !== !next.response) return false;
4112
+ if (prev.response && next.response) {
4113
+ if (prev.response.text !== next.response.text || prev.response.isStreaming !== next.response.isStreaming || prev.response.messageId !== next.response.messageId || prev.response.annotations?.length !== next.response.annotations?.length) return false;
4114
+ }
4115
+ }
4105
4116
  if (prev.openAnnotationRequest !== next.openAnnotationRequest) return false;
4106
4117
  if (prev.hasActiveFollowUpAnnotations !== next.hasActiveFollowUpAnnotations) return false;
4107
4118
  return prev.sessionId === next.sessionId && prev.turnId === next.turnId;
@@ -5361,6 +5372,24 @@ function handleTextDelta(state, event) {
5361
5372
  const streamingIndex = findStreamingMessage(session.messages, event.turnId);
5362
5373
  if (streamingIndex !== -1) {
5363
5374
  const currentMsg = session.messages[streamingIndex];
5375
+ if (currentMsg.isIntermediate && !event.isIntermediate) {
5376
+ const closedSession = updateMessageAt(session, streamingIndex, {
5377
+ isStreaming: false
5378
+ });
5379
+ const newMessage2 = {
5380
+ id: generateMessageId2(),
5381
+ role: "assistant",
5382
+ content: event.delta,
5383
+ timestamp: event.timestamp ?? Date.now(),
5384
+ isStreaming: true,
5385
+ isPending: true,
5386
+ turnId: event.turnId
5387
+ };
5388
+ return {
5389
+ session: appendMessage(closedSession, newMessage2, false),
5390
+ streaming: { content: event.delta, turnId: event.turnId }
5391
+ };
5392
+ }
5364
5393
  const updatedSession = updateMessageAt(session, streamingIndex, {
5365
5394
  content: currentMsg.content + event.delta
5366
5395
  });
@@ -5373,7 +5402,8 @@ function handleTextDelta(state, event) {
5373
5402
  timestamp: event.timestamp ?? Date.now(),
5374
5403
  isStreaming: true,
5375
5404
  isPending: true,
5376
- turnId: event.turnId
5405
+ turnId: event.turnId,
5406
+ ...event.isIntermediate ? { isIntermediate: true } : {}
5377
5407
  };
5378
5408
  return {
5379
5409
  session: appendMessage(session, newMessage, false),
@@ -5383,12 +5413,15 @@ function handleTextDelta(state, event) {
5383
5413
  function handleTextComplete(state, event) {
5384
5414
  const { session, streaming } = state;
5385
5415
  let msgIndex = findStreamingMessage(session.messages, event.turnId);
5416
+ if (msgIndex !== -1 && event.isIntermediate && !session.messages[msgIndex].isIntermediate) {
5417
+ msgIndex = -1;
5418
+ }
5386
5419
  if (msgIndex === -1) {
5387
5420
  msgIndex = findAssistantMessage(session.messages, event.turnId);
5388
5421
  }
5389
5422
  if (msgIndex !== -1) {
5390
5423
  const existingMsg = session.messages[msgIndex];
5391
- if (!existingMsg.isStreaming && existingMsg.isIntermediate && event.isIntermediate) {
5424
+ if (!existingMsg.isStreaming && existingMsg.isIntermediate && !existingMsg.isPending) {
5392
5425
  msgIndex = -1;
5393
5426
  }
5394
5427
  }
@@ -6456,14 +6489,16 @@ function mapCoreEventToProcessorEvent(coreEvent, sessionId) {
6456
6489
  type: "text_delta",
6457
6490
  sessionId,
6458
6491
  delta: coreEvent.text,
6459
- turnId: coreEvent.turnId
6492
+ turnId: coreEvent.turnId,
6493
+ isIntermediate: true
6460
6494
  };
6461
6495
  case "reasoning":
6462
6496
  return {
6463
6497
  type: "text_complete",
6464
6498
  sessionId,
6465
6499
  text: coreEvent.text,
6466
- turnId: coreEvent.turnId
6500
+ turnId: coreEvent.turnId,
6501
+ isIntermediate: true
6467
6502
  };
6468
6503
  case "permission_response":
6469
6504
  return {
@@ -6503,7 +6538,8 @@ function mapTimelineEnvelopeToProcessorEvent(envelope) {
6503
6538
  sessionId,
6504
6539
  delta: item.text,
6505
6540
  turnId: item.turnId,
6506
- timestamp
6541
+ timestamp,
6542
+ ...item.type === "reasoning_delta" && { isIntermediate: true }
6507
6543
  };
6508
6544
  case "assistant_message":
6509
6545
  case "reasoning":
@@ -6513,7 +6549,8 @@ function mapTimelineEnvelopeToProcessorEvent(envelope) {
6513
6549
  text: item.text,
6514
6550
  turnId: item.turnId,
6515
6551
  timestamp,
6516
- messageId: item.messageId
6552
+ messageId: item.messageId,
6553
+ ...item.type === "reasoning" && { isIntermediate: true }
6517
6554
  };
6518
6555
  case "tool_call":
6519
6556
  return {
@@ -7189,6 +7226,17 @@ function useAgentChatSession(options) {
7189
7226
  function useTimelineAgentChatSession(options) {
7190
7227
  const { runtime, workspaceId = "local-workspace", workspaceName = "Local Workspace" } = options;
7191
7228
  const [timeline, setTimeline] = useState12([]);
7229
+ const [sessionState, setSessionState] = useState12(() => ({
7230
+ session: {
7231
+ id: runtime.sessionId,
7232
+ workspaceId,
7233
+ workspaceName,
7234
+ lastMessageAt: 0,
7235
+ messages: [],
7236
+ isProcessing: false
7237
+ },
7238
+ streaming: null
7239
+ }));
7192
7240
  const [capabilityReport, setCapabilityReport] = useState12(null);
7193
7241
  const [error, setError] = useState12(null);
7194
7242
  const [isReconnecting, setIsReconnecting] = useState12(false);
@@ -7199,6 +7247,11 @@ function useTimelineAgentChatSession(options) {
7199
7247
  const onEvent = (envelope) => {
7200
7248
  lastCursorRef.current = { epoch: envelope.epoch, afterSeq: envelope.seq };
7201
7249
  setTimeline((current) => mergeTimeline(current, [envelope]));
7250
+ if (isChatTranscriptTimelineEnvelope(envelope)) {
7251
+ setSessionState(
7252
+ (current) => processEvent(current, mapTimelineEnvelopeToProcessorEvent(envelope)).state
7253
+ );
7254
+ }
7202
7255
  setIsReconnecting(false);
7203
7256
  };
7204
7257
  const onError = (err) => {
@@ -7213,6 +7266,16 @@ function useTimelineAgentChatSession(options) {
7213
7266
  if (catchupAbort) return;
7214
7267
  if (result.items.length > 0) {
7215
7268
  setTimeline((current) => mergeTimeline(current, result.items));
7269
+ const chatEvents = result.items.filter(isChatTranscriptTimelineEnvelope).sort((a, b) => a.epoch !== b.epoch ? a.epoch.localeCompare(b.epoch) : a.seq - b.seq);
7270
+ if (chatEvents.length > 0) {
7271
+ setSessionState((current) => {
7272
+ let s = current;
7273
+ for (const env of chatEvents) {
7274
+ s = processEvent(s, mapTimelineEnvelopeToProcessorEvent(env)).state;
7275
+ }
7276
+ return s;
7277
+ });
7278
+ }
7216
7279
  }
7217
7280
  if (result.hasGap) {
7218
7281
  setHasGap(true);
@@ -7251,25 +7314,23 @@ function useTimelineAgentChatSession(options) {
7251
7314
  const respondToPermission = useCallback9(async (requestId, allowed, remember) => {
7252
7315
  await runtime.commands.respondToPermission(requestId, allowed, remember);
7253
7316
  }, [runtime]);
7254
- const model = useMemo8(
7255
- () => createTimelineAgentChatPanelModel({
7256
- timeline,
7257
- sessionId: runtime.sessionId,
7258
- workspaceId,
7259
- workspaceName,
7260
- runtimeState: runtime.getState(),
7261
- capabilityReport,
7262
- error
7263
- }),
7264
- [timeline, runtime, workspaceId, workspaceName, capabilityReport, error]
7317
+ const turns = useMemo8(
7318
+ () => groupMessagesByTurn(sessionState.session.messages),
7319
+ [sessionState]
7265
7320
  );
7321
+ const runtimeState = runtime.getState();
7322
+ const isRunning = runtimeState.status === "running" || runtimeState.status === "preflighting" || runtimeState.status === "starting" || runtimeState.status === "waiting_for_permission";
7266
7323
  const isConnected = runtime.events.isConnected();
7267
7324
  return {
7268
- ...model,
7325
+ session: sessionState.session,
7326
+ turns,
7269
7327
  timeline,
7328
+ isRunning,
7270
7329
  isConnected,
7271
7330
  isReconnecting,
7272
7331
  hasGap,
7332
+ capabilityReport,
7333
+ error,
7273
7334
  sendMessage,
7274
7335
  abort,
7275
7336
  respondToPermission
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percena/weft-react",
3
- "version": "0.1.5-next.0",
3
+ "version": "0.1.5-next.2",
4
4
  "license": "MIT",
5
5
  "description": "Weft React — TimelineAgentChatPanel + headless hooks (useAgentSession) with precompiled CSS; no Tailwind setup required in the host",
6
6
  "type": "module",
@@ -47,17 +47,17 @@
47
47
  "remark-gfm": "^4.0.0",
48
48
  "remark-math": "^6.0.0",
49
49
  "zod": "^3.0.0",
50
- "@percena/weft-client": "^0.1.0"
50
+ "@percena/weft-client": "^0.2.0-next.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@tailwindcss/cli": "^4.0.0",
54
54
  "@tailwindcss/typography": "^0.5.16",
55
55
  "@types/react": "^19.2.14",
56
56
  "tailwindcss": "^4.0.0",
57
- "@weft/provider-flitro": "0.1.0",
58
- "@weft/ui": "0.1.0",
59
57
  "@weft/local-chat": "0.1.0",
60
- "@weft/runtime-core": "0.1.0"
58
+ "@weft/runtime-core": "0.1.0",
59
+ "@weft/provider-flitro": "0.1.0",
60
+ "@weft/ui": "0.1.0"
61
61
  },
62
62
  "keywords": [
63
63
  "weft",