@blade-hq/agent-kit 1.0.27 → 1.0.28

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.
Files changed (48) hide show
  1. package/dist/{chunk-X2NJKKXR.js → chunk-BGNKHPCD.js} +213 -40
  2. package/dist/chunk-BGNKHPCD.js.map +1 -0
  3. package/dist/{chunk-M72OL7ZJ.js → chunk-HCQDDDOX.js} +2 -2
  4. package/dist/{chunk-TE6G6NVX.js → chunk-OGHUIKCY.js} +3 -3
  5. package/dist/{chunk-C2FKVGJU.js → chunk-PYYDXV2A.js} +2 -2
  6. package/dist/{chunk-JU5SL4HP.js → chunk-TVWC3PED.js} +2 -2
  7. package/dist/{chunk-SBASG3M2.js → chunk-UWNQ4PXK.js} +4 -1101
  8. package/dist/chunk-UWNQ4PXK.js.map +1 -0
  9. package/dist/{chunk-34GDDOWY.js → chunk-YJ6BX3FU.js} +1379 -31
  10. package/dist/chunk-YJ6BX3FU.js.map +1 -0
  11. package/dist/client/blade-client.d.ts +2 -0
  12. package/dist/client/index.d.ts +1 -0
  13. package/dist/client/index.js +3 -1
  14. package/dist/client/resources/app-dev-templates.d.ts +93 -0
  15. package/dist/client/resources/models.d.ts +3 -0
  16. package/dist/{react → client/shared}/projection/builder.d.ts +1 -1
  17. package/dist/{react → client/shared}/projection/helpers.d.ts +1 -1
  18. package/dist/client/shared/projection/history.d.ts +12 -0
  19. package/dist/{react → client/shared}/projection/index.d.ts +1 -0
  20. package/dist/{react → client/shared}/projection/state.d.ts +1 -1
  21. package/dist/client/types/index.d.ts +2 -2
  22. package/dist/client/types/rest.d.ts +1010 -29
  23. package/dist/client/types/socket-events.d.ts +18 -132
  24. package/dist/react/api/app-dev-templates.d.ts +5 -0
  25. package/dist/react/api/model-config.d.ts +68 -0
  26. package/dist/react/api/models.d.ts +2 -0
  27. package/dist/react/api/published-apps.js +3 -3
  28. package/dist/react/api/sessions.js +2 -2
  29. package/dist/react/components/chat/ChatView.d.ts +3 -1
  30. package/dist/react/components/chat/MessageList.d.ts +3 -1
  31. package/dist/react/components/chat/index.js +5 -5
  32. package/dist/react/components/chat/scroll-anchor.d.ts +8 -0
  33. package/dist/react/components/plan/index.js +4 -4
  34. package/dist/react/components/session/index.js +3 -3
  35. package/dist/react/components/workspace/index.js +3 -3
  36. package/dist/react/hooks/use-model-preferences.d.ts +1 -0
  37. package/dist/react/hooks/use-resolved-model.d.ts +1 -0
  38. package/dist/react/index.d.ts +3 -0
  39. package/dist/react/index.js +87 -7
  40. package/dist/react/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/chunk-34GDDOWY.js.map +0 -1
  43. package/dist/chunk-SBASG3M2.js.map +0 -1
  44. package/dist/chunk-X2NJKKXR.js.map +0 -1
  45. /package/dist/{chunk-M72OL7ZJ.js.map → chunk-HCQDDDOX.js.map} +0 -0
  46. /package/dist/{chunk-TE6G6NVX.js.map → chunk-OGHUIKCY.js.map} +0 -0
  47. /package/dist/{chunk-C2FKVGJU.js.map → chunk-PYYDXV2A.js.map} +0 -0
  48. /package/dist/{chunk-JU5SL4HP.js.map → chunk-TVWC3PED.js.map} +0 -0
@@ -13,10 +13,10 @@ import {
13
13
  getCodeLanguageFromFilename,
14
14
  parseAskUserQuestion,
15
15
  useHighlightedCodeHtml
16
- } from "./chunk-TE6G6NVX.js";
16
+ } from "./chunk-OGHUIKCY.js";
17
17
  import {
18
18
  resolveSessionFilePreviewTarget
19
- } from "./chunk-C2FKVGJU.js";
19
+ } from "./chunk-PYYDXV2A.js";
20
20
  import {
21
21
  apiFetchResponse,
22
22
  buildMessageContent,
@@ -60,7 +60,7 @@ import {
60
60
  useUiBridgeStore,
61
61
  useUiStore,
62
62
  writeFile
63
- } from "./chunk-SBASG3M2.js";
63
+ } from "./chunk-UWNQ4PXK.js";
64
64
  import {
65
65
  registerBridgeIframe,
66
66
  tapBridgeEvent
@@ -69,7 +69,7 @@ import {
69
69
  ModelOption,
70
70
  ModelsConfig,
71
71
  ModelsResource
72
- } from "./chunk-34GDDOWY.js";
72
+ } from "./chunk-YJ6BX3FU.js";
73
73
  import {
74
74
  cn,
75
75
  copyToClipboard
@@ -704,6 +704,7 @@ function useModelConfig() {
704
704
  return {
705
705
  models: data?.models ?? [],
706
706
  defaultModel: data?.default ?? "",
707
+ thinkingAvailable: data?.thinkingAvailable ?? false,
707
708
  isLoading
708
709
  };
709
710
  }
@@ -1000,9 +1001,9 @@ function useInputHistory(sessionId) {
1000
1001
  // src/react/hooks/use-resolved-model.ts
1001
1002
  import { useMemo as useMemo3 } from "react";
1002
1003
  function useResolvedModel(activeSessionModel) {
1003
- const { models, defaultModel, isLoading } = useModelConfig();
1004
+ const { models, defaultModel, thinkingAvailable, isLoading } = useModelConfig();
1004
1005
  const { preferredModel } = usePreferredModel();
1005
- return useMemo3(() => {
1006
+ const resolved = useMemo3(() => {
1006
1007
  if (isLoading) {
1007
1008
  const optimistic = activeSessionModel || preferredModel || defaultModel || "";
1008
1009
  return {
@@ -1027,6 +1028,8 @@ function useResolvedModel(activeSessionModel) {
1027
1028
  }
1028
1029
  return { resolvedModel: "", hasAvailableModel: false, isLoading };
1029
1030
  }, [activeSessionModel, preferredModel, defaultModel, models, isLoading]);
1031
+ const selectedThinkingAvailable = models.find((model) => model.id === resolved.resolvedModel)?.thinkingAvailable ?? thinkingAvailable;
1032
+ return { ...resolved, thinkingAvailable: selectedThinkingAvailable };
1030
1033
  }
1031
1034
 
1032
1035
  // src/react/lib/attachment-upload.ts
@@ -2935,7 +2938,12 @@ function ChatInput({
2935
2938
  const draftAppends = useUiBridgeStore(
2936
2939
  (state) => activeSessionId ? state.draftAppends[activeSessionId] : void 0
2937
2940
  );
2938
- const { resolvedModel, hasAvailableModel, isLoading: isModelLoading } = useResolvedModel(activeSessionModel);
2941
+ const {
2942
+ resolvedModel,
2943
+ hasAvailableModel,
2944
+ thinkingAvailable,
2945
+ isLoading: isModelLoading
2946
+ } = useResolvedModel(activeSessionModel);
2939
2947
  useEffect7(() => {
2940
2948
  setSelectedModel(resolvedModel);
2941
2949
  }, [resolvedModel]);
@@ -3528,7 +3536,7 @@ function ChatInput({
3528
3536
  onSend(nextPayload, {
3529
3537
  targetSessionId: uploadTargetSessionId,
3530
3538
  model: selectedModel || null,
3531
- thinkingOverride: sessionThinking != null || thinkingPreference != null ? thinkingEnabled : void 0
3539
+ thinkingOverride: thinkingAvailable && (sessionThinking != null || thinkingPreference != null) ? thinkingEnabled : void 0
3532
3540
  });
3533
3541
  if (text.trim()) {
3534
3542
  inputHistory.addEntry(text);
@@ -3859,7 +3867,7 @@ function ChatInput({
3859
3867
  }
3860
3868
  )
3861
3869
  ] }) : null,
3862
- /* @__PURE__ */ jsx10(
3870
+ thinkingAvailable ? /* @__PURE__ */ jsx10(
3863
3871
  ThinkingToggle,
3864
3872
  {
3865
3873
  enabled: thinkingEnabled,
@@ -3868,7 +3876,7 @@ function ChatInput({
3868
3876
  setPreferredThinkingEnabled(next);
3869
3877
  }
3870
3878
  }
3871
- ),
3879
+ ) : null,
3872
3880
  /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 px-3 py-2", children: [
3873
3881
  /* @__PURE__ */ jsx10(Sparkles2, { size: 13 }),
3874
3882
  /* @__PURE__ */ jsx10("span", { className: "shrink-0", children: "\u6A21\u578B" }),
@@ -8218,6 +8226,21 @@ function TurnNavRail({
8218
8226
  );
8219
8227
  }
8220
8228
 
8229
+ // src/react/components/chat/scroll-anchor.ts
8230
+ var ENTRY_SCROLL_MARGIN_PX = 24;
8231
+ function normalizeScrollEntryKey(value) {
8232
+ const text = value?.trim();
8233
+ return text ? text : null;
8234
+ }
8235
+ function calculateAnchoredScrollTop({
8236
+ scrollTop,
8237
+ containerTop,
8238
+ targetTop,
8239
+ margin = ENTRY_SCROLL_MARGIN_PX
8240
+ }) {
8241
+ return Math.max(0, scrollTop + targetTop - containerTop - margin);
8242
+ }
8243
+
8221
8244
  // src/react/components/chat/MessageList.tsx
8222
8245
  import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
8223
8246
  function parseModeChange(message) {
@@ -8296,13 +8319,20 @@ function findScrollContainer(start) {
8296
8319
  }
8297
8320
  return null;
8298
8321
  }
8322
+ function findEntryAnchor(root, entryId) {
8323
+ return Array.from(root.querySelectorAll("[data-entry-id]")).find(
8324
+ (el) => el.dataset.entryId === entryId
8325
+ ) ?? null;
8326
+ }
8299
8327
  function MessageList({
8300
8328
  sessionId,
8301
8329
  messages: rawMessages,
8302
8330
  onAnswer,
8303
8331
  sessionStatus,
8304
8332
  onConfirmPlan,
8305
- customization
8333
+ customization,
8334
+ scrollToStartKey,
8335
+ scrollToEntryKey
8306
8336
  }) {
8307
8337
  const messages = Array.isArray(rawMessages) ? rawMessages : [];
8308
8338
  const agentLoops = useChatStore((state) => state.agentLoops[sessionId]);
@@ -8381,7 +8411,8 @@ function MessageList({
8381
8411
  blocks.push({
8382
8412
  type: "message",
8383
8413
  message,
8384
- key: message.entry_id ?? `${message.role}-${blocks.length}`
8414
+ key: message.entry_id ?? `${message.role}-${blocks.length}`,
8415
+ anchorId: message.entry_id ? `chat-entry-${message.entry_id}` : null
8385
8416
  });
8386
8417
  }
8387
8418
  flushAssistant();
@@ -8429,9 +8460,18 @@ function MessageList({
8429
8460
  ) ?? null,
8430
8461
  [lastTurnId, renderBlocks]
8431
8462
  );
8463
+ const scrollEntryId = useMemo18(() => normalizeScrollEntryKey(scrollToEntryKey), [scrollToEntryKey]);
8464
+ const shouldAnchorEntry = Boolean(scrollEntryId);
8465
+ const hasMessages = messages.length > 0;
8432
8466
  const containerRef = useRef13(null);
8433
8467
  const scrollContainerRef = useRef13(null);
8434
8468
  const frameRef = useRef13(null);
8469
+ const startScrollFrameRef = useRef13(null);
8470
+ const startScrollTimeoutRef = useRef13(null);
8471
+ const entryScrollFrameRef = useRef13(null);
8472
+ const entryScrollTimeoutRefs = useRef13([]);
8473
+ const appliedStartScrollKeyRef = useRef13(null);
8474
+ const appliedEntryScrollKeyRef = useRef13(null);
8435
8475
  const [activeTurnId, setActiveTurnId] = useState21(lastTurnId);
8436
8476
  const layoutSignature = useMemo18(
8437
8477
  () => getMessagesMeasureSignature(messages),
@@ -8492,6 +8532,123 @@ function MessageList({
8492
8532
  useEffect18(() => {
8493
8533
  scheduleMeasure();
8494
8534
  }, [lastTurnId, layoutSignature, scheduleMeasure]);
8535
+ const scrollToSessionStart = useEffectEvent4(() => {
8536
+ const container = scrollContainerRef.current ?? findScrollContainer(containerRef.current);
8537
+ if (!container) return;
8538
+ container.scrollTo({ top: 0, behavior: "auto" });
8539
+ setActiveTurnId(null);
8540
+ });
8541
+ useEffect18(() => {
8542
+ if (!scrollToStartKey || !hasMessages) return;
8543
+ const applyKey = scrollToStartKey;
8544
+ if (appliedStartScrollKeyRef.current === applyKey) return;
8545
+ appliedStartScrollKeyRef.current = applyKey;
8546
+ if (startScrollFrameRef.current != null) {
8547
+ window.cancelAnimationFrame(startScrollFrameRef.current);
8548
+ }
8549
+ if (startScrollTimeoutRef.current != null) {
8550
+ window.clearTimeout(startScrollTimeoutRef.current);
8551
+ }
8552
+ startScrollFrameRef.current = window.requestAnimationFrame(() => {
8553
+ startScrollFrameRef.current = null;
8554
+ scrollToSessionStart();
8555
+ startScrollTimeoutRef.current = window.setTimeout(() => {
8556
+ startScrollTimeoutRef.current = null;
8557
+ scrollToSessionStart();
8558
+ }, 80);
8559
+ });
8560
+ return () => {
8561
+ if (startScrollFrameRef.current != null) {
8562
+ window.cancelAnimationFrame(startScrollFrameRef.current);
8563
+ startScrollFrameRef.current = null;
8564
+ }
8565
+ if (startScrollTimeoutRef.current != null) {
8566
+ window.clearTimeout(startScrollTimeoutRef.current);
8567
+ startScrollTimeoutRef.current = null;
8568
+ }
8569
+ };
8570
+ }, [hasMessages, scrollToSessionStart, scrollToStartKey]);
8571
+ const scrollToEntry = useEffectEvent4((entryId) => {
8572
+ const root = containerRef.current;
8573
+ if (!root) return false;
8574
+ const target = findEntryAnchor(root, entryId);
8575
+ if (!target) return false;
8576
+ stickContextRef.current?.stopScroll();
8577
+ const container = scrollContainerRef.current ?? findScrollContainer(root);
8578
+ if (container) {
8579
+ const containerRect = container.getBoundingClientRect();
8580
+ const targetRect = target.getBoundingClientRect();
8581
+ container.scrollTo({
8582
+ top: calculateAnchoredScrollTop({
8583
+ scrollTop: container.scrollTop,
8584
+ containerTop: containerRect.top,
8585
+ targetTop: targetRect.top
8586
+ }),
8587
+ behavior: "auto"
8588
+ });
8589
+ } else {
8590
+ target.scrollIntoView({ behavior: "auto", block: "start" });
8591
+ }
8592
+ setActiveTurnId(target.dataset.turnId ?? null);
8593
+ return true;
8594
+ });
8595
+ const getAnchorTargetScrollTop = useCallback12(
8596
+ (defaultTargetScrollTop, { scrollElement, contentElement }) => {
8597
+ if (!scrollEntryId) return defaultTargetScrollTop;
8598
+ const target = findEntryAnchor(contentElement, scrollEntryId);
8599
+ if (!target) return defaultTargetScrollTop;
8600
+ const containerRect = scrollElement.getBoundingClientRect();
8601
+ const targetRect = target.getBoundingClientRect();
8602
+ return calculateAnchoredScrollTop({
8603
+ scrollTop: scrollElement.scrollTop,
8604
+ containerTop: containerRect.top,
8605
+ targetTop: targetRect.top
8606
+ });
8607
+ },
8608
+ [scrollEntryId]
8609
+ );
8610
+ useEffect18(() => {
8611
+ if (!scrollEntryId || !hasMessages) return;
8612
+ const entryId = scrollEntryId;
8613
+ const applyKey = scrollEntryId;
8614
+ if (appliedEntryScrollKeyRef.current === applyKey) return;
8615
+ appliedEntryScrollKeyRef.current = applyKey;
8616
+ if (entryScrollFrameRef.current != null) {
8617
+ window.cancelAnimationFrame(entryScrollFrameRef.current);
8618
+ }
8619
+ for (const timeoutId of entryScrollTimeoutRefs.current) {
8620
+ window.clearTimeout(timeoutId);
8621
+ }
8622
+ entryScrollTimeoutRefs.current = [];
8623
+ entryScrollFrameRef.current = window.requestAnimationFrame(() => {
8624
+ entryScrollFrameRef.current = null;
8625
+ const attempts = [0, 60, 160, 360, 700, 1200];
8626
+ let matched = false;
8627
+ for (const delay of attempts) {
8628
+ const timeoutId = window.setTimeout(() => {
8629
+ matched = scrollToEntry(entryId) || matched;
8630
+ }, delay);
8631
+ entryScrollTimeoutRefs.current.push(timeoutId);
8632
+ }
8633
+ const fallbackId = window.setTimeout(() => {
8634
+ entryScrollTimeoutRefs.current = [];
8635
+ if (!matched && !scrollToEntry(entryId)) {
8636
+ scrollToSessionStart();
8637
+ }
8638
+ }, 1400);
8639
+ entryScrollTimeoutRefs.current.push(fallbackId);
8640
+ });
8641
+ return () => {
8642
+ if (entryScrollFrameRef.current != null) {
8643
+ window.cancelAnimationFrame(entryScrollFrameRef.current);
8644
+ entryScrollFrameRef.current = null;
8645
+ }
8646
+ for (const timeoutId of entryScrollTimeoutRefs.current) {
8647
+ window.clearTimeout(timeoutId);
8648
+ }
8649
+ entryScrollTimeoutRefs.current = [];
8650
+ };
8651
+ }, [hasMessages, scrollEntryId, scrollToEntry, scrollToSessionStart]);
8495
8652
  const handleSelectTurn = useCallback12((turnId) => {
8496
8653
  document.getElementById(turnId)?.scrollIntoView({ behavior: "smooth", block: "start" });
8497
8654
  }, []);
@@ -8514,8 +8671,9 @@ function MessageList({
8514
8671
  {
8515
8672
  contextRef: stickContextRef,
8516
8673
  className: "h-full overflow-y-hidden",
8517
- initial: "instant",
8674
+ initial: shouldAnchorEntry ? "instant" : "instant",
8518
8675
  resize: stickyTurn ? { damping: 0.8, stiffness: 0.08, mass: 1 } : "instant",
8676
+ targetScrollTop: shouldAnchorEntry ? getAnchorTargetScrollTop : void 0,
8519
8677
  children: [
8520
8678
  /* @__PURE__ */ jsx37(StickToBottom.Content, { className: "px-5 py-6", children: /* @__PURE__ */ jsx37(
8521
8679
  MessageListContent,
@@ -8536,7 +8694,8 @@ function MessageList({
8536
8694
  ) }),
8537
8695
  /* @__PURE__ */ jsx37(ScrollToBottomButton, {})
8538
8696
  ]
8539
- }
8697
+ },
8698
+ scrollEntryId ?? "latest"
8540
8699
  )
8541
8700
  ]
8542
8701
  }
@@ -8581,31 +8740,40 @@ function MessageListContent({
8581
8740
  /* @__PURE__ */ jsx37("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
8582
8741
  ] }) : renderBlocks.map((block, blockIndex) => {
8583
8742
  if (block.type === "message") {
8584
- return /* @__PURE__ */ jsx37("div", { className: "msg-animate", children: isUserMessage(block.message) ? customization?.components?.UserMessage ? /* @__PURE__ */ jsx37(
8585
- customization.components.UserMessage,
8586
- {
8587
- message: block.message,
8588
- className: customization.classNames?.userMessage
8589
- }
8590
- ) : /* @__PURE__ */ jsx37(
8591
- UserMessageBubble,
8592
- {
8593
- message: block.message,
8594
- className: customization?.classNames?.userMessage
8595
- }
8596
- ) : isErrorMessage(block.message) ? customization?.components?.ErrorMessage ? /* @__PURE__ */ jsx37(
8597
- customization.components.ErrorMessage,
8598
- {
8599
- message: block.message,
8600
- className: customization.classNames?.errorMessage
8601
- }
8602
- ) : /* @__PURE__ */ jsx37(
8603
- ErrorMessageBlock,
8743
+ return /* @__PURE__ */ jsx37(
8744
+ "div",
8604
8745
  {
8605
- message: block.message,
8606
- className: customization?.classNames?.errorMessage
8607
- }
8608
- ) : null }, block.key);
8746
+ id: block.anchorId ?? void 0,
8747
+ "data-entry-id": block.message.entry_id,
8748
+ className: "msg-animate scroll-mt-6",
8749
+ children: isUserMessage(block.message) ? customization?.components?.UserMessage ? /* @__PURE__ */ jsx37(
8750
+ customization.components.UserMessage,
8751
+ {
8752
+ message: block.message,
8753
+ className: customization.classNames?.userMessage
8754
+ }
8755
+ ) : /* @__PURE__ */ jsx37(
8756
+ UserMessageBubble,
8757
+ {
8758
+ message: block.message,
8759
+ className: customization?.classNames?.userMessage
8760
+ }
8761
+ ) : isErrorMessage(block.message) ? customization?.components?.ErrorMessage ? /* @__PURE__ */ jsx37(
8762
+ customization.components.ErrorMessage,
8763
+ {
8764
+ message: block.message,
8765
+ className: customization.classNames?.errorMessage
8766
+ }
8767
+ ) : /* @__PURE__ */ jsx37(
8768
+ ErrorMessageBlock,
8769
+ {
8770
+ message: block.message,
8771
+ className: customization?.classNames?.errorMessage
8772
+ }
8773
+ ) : null
8774
+ },
8775
+ block.key
8776
+ );
8609
8777
  }
8610
8778
  if (block.type === "assistant_turn") {
8611
8779
  const nextBlock = renderBlocks[blockIndex + 1];
@@ -8621,6 +8789,7 @@ function MessageListContent({
8621
8789
  {
8622
8790
  id: block.anchorId,
8623
8791
  "data-turn-id": block.anchorId,
8792
+ "data-entry-id": block.messages[0]?.entry_id,
8624
8793
  className: "msg-animate flex scroll-mt-6 flex-col gap-4",
8625
8794
  children: [
8626
8795
  /* @__PURE__ */ jsx37(
@@ -8735,6 +8904,8 @@ function ChatView({
8735
8904
  inputLabel,
8736
8905
  onDoubleEscape,
8737
8906
  composerHint,
8907
+ scrollToStartKey,
8908
+ scrollToEntryKey,
8738
8909
  classNames,
8739
8910
  components,
8740
8911
  renderers
@@ -8789,7 +8960,9 @@ function ChatView({
8789
8960
  onAnswer: isViewer ? void 0 : (msg, toolCallId, answerData) => send(msg, mode, { tool_call_id: toolCallId, ...answerData }),
8790
8961
  sessionStatus,
8791
8962
  onConfirmPlan: isViewer ? void 0 : onConfirmPlan,
8792
- customization
8963
+ customization,
8964
+ scrollToStartKey,
8965
+ scrollToEntryKey
8793
8966
  },
8794
8967
  sessionId
8795
8968
  ),
@@ -8878,4 +9051,4 @@ use-stick-to-bottom/dist/StickToBottom.js:
8878
9051
  * Licensed under the MIT License. See License.txt in the project root for license information.
8879
9052
  *--------------------------------------------------------------------------------------------*)
8880
9053
  */
8881
- //# sourceMappingURL=chunk-X2NJKKXR.js.map
9054
+ //# sourceMappingURL=chunk-BGNKHPCD.js.map