@mcp-b/react-components 0.55.0 → 0.56.1

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 (44) hide show
  1. package/dist/{AgentToolRunsContext-GMe_hSnD.d.ts → AgentToolRunsContext-DT50htHj.d.ts} +4 -17
  2. package/dist/{EmployeeChrome-B605rpVz.d.ts → EmployeeChrome-B7Gt2Hpk.d.ts} +1 -1
  3. package/dist/{ThinkChatActivity-DENkintt.d.ts → ThinkChatActivity-B6dh3n0-.d.ts} +1 -1
  4. package/dist/components/agents-sdk/AgentChat.js +10 -14
  5. package/dist/components/agents-sdk/AgentToolRunsContext.d.ts +1 -1
  6. package/dist/components/agents-sdk/EmployeeApp.d.ts +1 -1
  7. package/dist/components/agents-sdk/EmployeeChrome.d.ts +1 -1
  8. package/dist/components/agents-sdk/EmployeeChrome.js +2 -2
  9. package/dist/components/agents-sdk/EmployeeSchedules.d.ts +1 -1
  10. package/dist/components/agents-sdk/EmployeeStartScreen.d.ts +1 -1
  11. package/dist/components/agents-sdk/ManagerOfficeScreens.d.ts +1 -1
  12. package/dist/components/agents-sdk/SubagentRunDetails.d.ts +1 -1
  13. package/dist/components/agents-sdk/SubagentRunIndicator.d.ts +1 -1
  14. package/dist/components/agents-sdk/SubagentRunList.d.ts +1 -1
  15. package/dist/components/agents-sdk/SubagentRunPanel.d.ts +3 -14
  16. package/dist/components/agents-sdk/SubagentRunPanel.js +11 -14
  17. package/dist/components/agents-sdk/SubagentRunSelector.d.ts +1 -1
  18. package/dist/components/agents-sdk/ThinkChat.d.ts +2 -9
  19. package/dist/components/agents-sdk/ThinkChat.js +3 -36
  20. package/dist/components/agents-sdk/ThinkChatActivity.d.ts +1 -1
  21. package/dist/components/agents-sdk/ThinkFullApp.d.ts +1 -1
  22. package/dist/components/agents-sdk/ThinkFullApp.js +1 -4
  23. package/dist/components/agents-sdk/ThinkThreadPicker.js +10 -7
  24. package/dist/components/nanites/AgentToolDetail.d.ts +1 -1
  25. package/dist/styles/agent-chat.css +30 -35
  26. package/dist/styles/app-icon.css +1 -2
  27. package/dist/styles/base.css +19 -10
  28. package/dist/styles/brand-loading.css +14 -0
  29. package/dist/styles/brand.test.ts +21 -0
  30. package/dist/styles/browser-replay.css +2 -2
  31. package/dist/styles/button.css +0 -2
  32. package/dist/styles/checkbox.css +1 -1
  33. package/dist/styles/composition-motion.css +13 -3
  34. package/dist/styles/github-motion-mark.css +2 -2
  35. package/dist/styles/responsive-sidebar-shell.css +11 -1
  36. package/dist/styles/search-field.css +1 -1
  37. package/dist/styles/tabs.css +1 -1
  38. package/dist/styles/think-routine.css +0 -2
  39. package/dist/styles/think-thread-picker.css +18 -5
  40. package/dist/styles/toggle-group.css +2 -2
  41. package/package.json +6 -6
  42. package/dist/SessionCompaction-CmTJFpzj.d.ts +0 -89
  43. package/dist/components/agents-sdk/SessionCompaction.d.ts +0 -2
  44. package/dist/components/agents-sdk/SessionCompaction.js +0 -140
@@ -3,7 +3,7 @@
3
3
  silhouette, so a transparent favicon casts its shape, not an invisible box. */
4
4
  .avatar.app-icon {
5
5
  --sigvelo-recipe-app-icon-size: var(--sigvelo-spacing-8);
6
- --sigvelo-recipe-app-icon-radius: 30%;
6
+ --sigvelo-recipe-app-icon-radius: var(--sigvelo-radius-md);
7
7
 
8
8
  position: relative;
9
9
  display: inline-flex;
@@ -28,7 +28,6 @@
28
28
 
29
29
  .app-icon[data-app-icon-size="sm"] {
30
30
  --sigvelo-recipe-app-icon-size: var(--sigvelo-spacing-6);
31
- --sigvelo-recipe-app-icon-radius: 37.5%;
32
31
  }
33
32
 
34
33
  .app-icon[data-app-icon-size="lg"] {
@@ -27,33 +27,32 @@
27
27
  * `round` in the block below. audit:css-recipes fails if that list falls out of
28
28
  * sync with the stylesheets.
29
29
  */
30
- :where(*) {
30
+ :where(*),
31
+ :where(*)::before,
32
+ :where(*)::after {
31
33
  corner-shape: var(--sigvelo-corner-shape);
32
34
  }
33
35
 
36
+ /* Pseudo-elements follow :where(), since they are invalid inside its selector list. */
34
37
  /* sigvelo-pill-shapes:start */
35
38
  :where(
36
39
  .approval__key,
37
40
  .avatar[data-shape="circle"],
38
41
  .avatar-group__count,
39
42
  .browser-run-panel__tab-glyph,
43
+ .browser-replay .replayer-mouse.touch-device,
40
44
  .chain-of-thought__search-result,
41
45
  .chain-of-thought__step-indicator,
42
46
  .codex-model-selector__trigger,
43
- .codex-model-selector__trigger::before,
44
47
  .empty-state__icon,
45
48
  .inline-citation__trigger,
46
49
  .media-preview-card__play,
47
50
  .manager-office__unread-dot,
48
- .manager-office__employee-nav::-webkit-scrollbar-thumb,
49
- :where([data-layout="chat"]) .manager-office__employee-link[aria-current="page"]::after,
50
- .menu__radio-indicator::before,
51
51
  .message-scroller__button,
52
52
  .plan-summary__trigger,
53
53
  .plan__step-indicator,
54
54
  .prompt-input__button[data-responsive-label],
55
55
  .prompt-input__context-trigger,
56
- .prompt-input__dictation[data-listening] .prompt-input__dictation-icon::before,
57
56
  .prompt-input__permissions-trigger,
58
57
  .prompt-input__recording-button,
59
58
  .prompt-input__stop,
@@ -62,24 +61,34 @@
62
61
  .radio,
63
62
  .radio__indicator,
64
63
  .scroll-area__thumb,
64
+ .sigvelo-floating-prompt > *,
65
65
  .slider__indicator,
66
66
  .slider__thumb,
67
67
  .slider__track,
68
68
  .stepper__indicator,
69
69
  .stepper__separator,
70
70
  .stepper__trigger,
71
- .subagent-run__milestone::before,
72
71
  .switch,
73
72
  .switch__thumb,
74
- .task__item::before,
75
73
  .test-results__test-status,
76
- .think-full-app__resizer::before,
77
74
  .think-routine__routine-status,
75
+ .think-thread-status-dot,
78
76
  [data-slot="meter-indicator"],
79
77
  [data-slot="meter-track"],
80
78
  [data-slot="progress-indicator"],
81
79
  [data-slot="progress-track"]
82
- ) {
80
+ ),
81
+ :where(.browser-replay .replayer-mouse)::after,
82
+ :where(.codex-model-selector__trigger)::before,
83
+ :where(.github-motion-mark)::before,
84
+ :where(.github-motion-mark)::after,
85
+ :where(.manager-office__employee-nav)::-webkit-scrollbar-thumb,
86
+ :where(:where([data-layout="chat"]) .manager-office__employee-link[aria-current="page"])::after,
87
+ :where(.menu__radio-indicator)::before,
88
+ :where(.prompt-input__dictation[data-listening] .prompt-input__dictation-icon)::before,
89
+ :where(.subagent-run__milestone)::before,
90
+ :where(.task__item)::before,
91
+ :where(.think-full-app__resizer)::before {
83
92
  --sigvelo-corner-shape: round;
84
93
  }
85
94
  /* sigvelo-pill-shapes:end */
@@ -47,6 +47,20 @@
47
47
  text-align: center;
48
48
  }
49
49
 
50
+ /* A brief pending mount should not flash a full-strength logo. Keep the
51
+ region and its announcement present; only the artwork and copy fade in. */
52
+ .brand-loading__mark,
53
+ .brand-loading__label {
54
+ transition: opacity var(--sigvelo-motion-duration-disclosure) var(--sigvelo-motion-ease-enter);
55
+ }
56
+
57
+ @starting-style {
58
+ .brand-loading__mark,
59
+ .brand-loading__label {
60
+ opacity: 0;
61
+ }
62
+ }
63
+
50
64
  @media (prefers-reduced-motion: reduce), (forced-colors: active) {
51
65
  .brand-loading__mark::after {
52
66
  display: none;
@@ -156,3 +156,24 @@ test("OS-following mode uses the same Sigvelo palette", () => {
156
156
  matchMedia("(prefers-color-scheme: dark)").matches ? "rgb(23, 23, 22)" : "rgb(247, 247, 244)",
157
157
  );
158
158
  });
159
+
160
+ test("shared Squircle corners preserve true circular pseudo-elements and button variant shape", () => {
161
+ root.removeAttribute("data-preset");
162
+ root.removeAttribute("data-style");
163
+ const primary = document.createElement("button");
164
+ primary.className = "button button--normal button--primary";
165
+ const destructive = document.createElement("button");
166
+ destructive.className = "button button--normal button--destructive";
167
+ const mark = document.createElement("span");
168
+ mark.className = "github-motion-mark";
169
+ document.body.append(primary, destructive, mark);
170
+ expect(getComputedStyle(destructive).borderRadius).toBe(getComputedStyle(primary).borderRadius);
171
+ if (CSS.supports("corner-shape", "superellipse(1.5)")) {
172
+ expect(getComputedStyle(primary).getPropertyValue("corner-shape")).toBe("superellipse(1.5)");
173
+ expect(getComputedStyle(primary, "::after").getPropertyValue("corner-shape")).toBe(
174
+ "superellipse(1.5)",
175
+ );
176
+ expect(getComputedStyle(mark, "::before").getPropertyValue("corner-shape")).toBe("round");
177
+ expect(getComputedStyle(mark, "::after").getPropertyValue("corner-shape")).toBe("round");
178
+ }
179
+ });
@@ -96,7 +96,7 @@
96
96
  width: 20px;
97
97
  height: 20px;
98
98
  background: var(--sigvelo-color-focus);
99
- border-radius: 100%;
99
+ border-radius: var(--sigvelo-radius-circle);
100
100
  transform: translate(-50%, -50%);
101
101
  opacity: 0.3;
102
102
  }
@@ -114,7 +114,7 @@
114
114
  border-color: transparent;
115
115
  border-style: solid;
116
116
  border-width: 4px;
117
- border-radius: 100%;
117
+ border-radius: var(--sigvelo-radius-circle);
118
118
  transition:
119
119
  left 0s linear,
120
120
  top 0s linear,
@@ -83,8 +83,6 @@
83
83
  }
84
84
 
85
85
  .button--normal.button--destructive {
86
- --sigvelo-recipe-button-radius: var(--sigvelo-radius-full);
87
-
88
86
  background-color: var(--sigvelo-color-danger-bg);
89
87
  color: var(--sigvelo-color-danger-text-on-bg);
90
88
  }
@@ -9,7 +9,7 @@
9
9
  flex-shrink: 0;
10
10
  border: var(--sigvelo-border-style) var(--sigvelo-border-width)
11
11
  var(--sigvelo-color-neutral-border-muted);
12
- border-radius: calc(var(--sigvelo-radius-md) - 4px);
12
+ border-radius: calc(var(--sigvelo-radius-md) - var(--sigvelo-spacing-1));
13
13
  background-color: var(--sigvelo-color-canvas);
14
14
  outline: none;
15
15
  cursor: pointer;
@@ -50,7 +50,7 @@
50
50
  }
51
51
  }
52
52
 
53
- /* React owns native snapshots; the class comes from <ViewTransition>. */
53
+ /* Snapshot ownership stays with the caller: Router for routes, React for local Suspense demos. */
54
54
  ::view-transition {
55
55
  pointer-events: none;
56
56
  }
@@ -60,12 +60,22 @@
60
60
  animation-timing-function: var(--sigvelo-motion-ease-change);
61
61
  }
62
62
 
63
+ /* A single Router-owned crossfade. Streaming and refetches never start this type. */
64
+ :root:active-view-transition-type(sigvelo-navigation)::view-transition-group(root) {
65
+ animation-duration: var(--sigvelo-motion-duration-disclosure);
66
+ animation-timing-function: var(--sigvelo-motion-ease-change);
67
+ }
68
+
63
69
  @media (prefers-reduced-motion: reduce) {
64
- ::view-transition-group(.sigvelo-layout-transition) {
70
+ ::view-transition-group(*),
71
+ ::view-transition-old(*),
72
+ ::view-transition-new(*) {
65
73
  animation: none !important;
66
74
  }
67
75
  }
68
76
 
69
- :root[data-motion="reduce"]::view-transition-group(.sigvelo-layout-transition) {
77
+ :root[data-motion="reduce"]::view-transition-group(*),
78
+ :root[data-motion="reduce"]::view-transition-old(*),
79
+ :root[data-motion="reduce"]::view-transition-new(*) {
70
80
  animation: none !important;
71
81
  }
@@ -26,7 +26,7 @@
26
26
  .github-motion-mark::before {
27
27
  inset: -0.22em;
28
28
  border: var(--sigvelo-border-style) var(--sigvelo-border-width) currentColor;
29
- border-radius: 999px;
29
+ border-radius: var(--sigvelo-radius-circle);
30
30
  opacity: 0.24;
31
31
  transform-origin: 50% 50%;
32
32
  animation: github-motion-mark-ring 2.6s ease-out infinite;
@@ -35,7 +35,7 @@
35
35
  .github-motion-mark::after {
36
36
  inline-size: 0.24em;
37
37
  block-size: 0.24em;
38
- border-radius: 999px;
38
+ border-radius: var(--sigvelo-radius-circle);
39
39
  background: currentColor;
40
40
  opacity: 0.55;
41
41
  transform: translate(0.48em, -0.46em);
@@ -272,6 +272,16 @@
272
272
  display: contents;
273
273
  }
274
274
 
275
+ /* Rich title triggers need the space left by the actual controls, rather
276
+ than the plain title's 45vw cap, to keep activity counts and a readable name. */
277
+ .app-top-bar[data-compact-leading="trigger"] {
278
+ grid-template-columns: auto var(--sigvelo-layout-track-fill) auto;
279
+ }
280
+
281
+ .app-top-bar[data-compact-leading="trigger"] .app-top-bar__title {
282
+ max-inline-size: 100%;
283
+ }
284
+
275
285
  .responsive-sidebar-shell__title-trigger {
276
286
  inline-size: auto;
277
287
  max-inline-size: 100%;
@@ -290,7 +300,7 @@
290
300
  box-shadow: var(--sigvelo-recipe-focus-shadow-inset);
291
301
  }
292
302
 
293
- .responsive-sidebar-shell__title-trigger span {
303
+ .responsive-sidebar-shell__title-trigger > span {
294
304
  min-inline-size: 0;
295
305
  overflow: hidden;
296
306
  text-overflow: ellipsis;
@@ -4,7 +4,7 @@
4
4
  --sigvelo-recipe-control-font-size: var(--sigvelo-density-compact-control-font-size);
5
5
  --sigvelo-recipe-control-line-height: var(--sigvelo-density-compact-control-line-height);
6
6
  --sigvelo-recipe-control-icon-size: var(--sigvelo-density-compact-control-icon-size);
7
- --sigvelo-recipe-control-radius: var(--sigvelo-radius-sm);
7
+ --sigvelo-recipe-control-radius: var(--sigvelo-radius-xl);
8
8
  --sigvelo-recipe-focus-shadow: var(--sigvelo-recipe-focus-shadow-inset);
9
9
  --sigvelo-focus-offset: -2px;
10
10
  }
@@ -148,7 +148,7 @@
148
148
 
149
149
  .tabs--bordered .tabs__tab {
150
150
  border-block-end: none;
151
- border-radius: calc(var(--sigvelo-radius-md) - 2px);
151
+ border-radius: calc(var(--sigvelo-radius-md) - var(--sigvelo-spacing-0-5));
152
152
  margin-block-end: 0;
153
153
  }
154
154
 
@@ -79,8 +79,6 @@
79
79
  }
80
80
 
81
81
  .think-routine__search {
82
- --sigvelo-recipe-control-radius: var(--sigvelo-radius-full);
83
-
84
82
  display: grid;
85
83
  gap: var(--sigvelo-spacing-1-5);
86
84
  margin-block-end: var(--sigvelo-spacing-2);
@@ -43,6 +43,7 @@
43
43
  }
44
44
 
45
45
  .think-thread-status-indicators {
46
+ flex: 0 0 auto;
46
47
  min-inline-size: 0;
47
48
  display: inline-flex;
48
49
  align-items: center;
@@ -66,6 +67,18 @@
66
67
  block-size: var(--sigvelo-density-compact-control-icon-size);
67
68
  }
68
69
 
70
+ .think-thread-status-dot {
71
+ flex: none;
72
+ inline-size: var(--sigvelo-spacing-1-5);
73
+ block-size: var(--sigvelo-spacing-1-5);
74
+ border-radius: var(--sigvelo-radius-full);
75
+ background: currentColor;
76
+ }
77
+
78
+ .think-thread-status-indicators__item .think-thread-status-dot {
79
+ margin-inline: var(--sigvelo-spacing-1);
80
+ }
81
+
69
82
  .think-thread-status-indicators__item[data-status="running"] {
70
83
  color: var(--sigvelo-color-primary-text);
71
84
  }
@@ -260,11 +273,6 @@
260
273
  color: var(--sigvelo-color-primary-text);
261
274
  }
262
275
 
263
- /* The six statuses this rule used to list were `isThreadBusy` written out a
264
- second time, and those are exactly the ones that render SpinnerIcon, which
265
- spins itself. A DotIcon never needed the rule and would have started turning
266
- the moment the two lists disagreed. */
267
-
268
276
  .think-thread-picker__thread[data-thread-status="error"] .think-thread-picker__thread-status {
269
277
  color: var(--sigvelo-color-danger-text);
270
278
  }
@@ -343,6 +351,11 @@
343
351
  }
344
352
 
345
353
  @media (forced-colors: active) {
354
+ .think-thread-status-dot {
355
+ forced-color-adjust: none;
356
+ background: CanvasText;
357
+ }
358
+
346
359
  .think-thread-picker {
347
360
  border-inline-end: var(--sigvelo-border-style) var(--sigvelo-border-width) canvastext;
348
361
  background: canvas;
@@ -19,7 +19,7 @@
19
19
  .toggle-group__item {
20
20
  color: var(--sigvelo-color-text-muted);
21
21
  border: none;
22
- border-radius: calc(var(--sigvelo-radius-md) - 2px);
22
+ border-radius: calc(var(--sigvelo-radius-md) - var(--sigvelo-spacing-0-5));
23
23
  }
24
24
 
25
25
  .toggle-group__item:hover:not([data-disabled]) {
@@ -65,7 +65,7 @@
65
65
  inset-block: var(--sigvelo-spacing-1);
66
66
  inline-size: var(--toggle-group-thumb-size, 0);
67
67
  translate: var(--toggle-group-thumb-start, 0) 0;
68
- border-radius: calc(var(--sigvelo-radius-md) - 2px);
68
+ border-radius: calc(var(--sigvelo-radius-md) - var(--sigvelo-spacing-0-5));
69
69
  background-color: var(--sigvelo-color-canvas);
70
70
  box-shadow: var(--sigvelo-elevation-raised);
71
71
  transition:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/react-components",
3
- "version": "0.55.0",
3
+ "version": "0.56.1",
4
4
  "description": "MCP-B React components built on Base UI and shared design tokens, including Cloudflare Think chat primitives.",
5
5
  "homepage": "https://design-system.sigvelo.com",
6
6
  "bugs": {
@@ -54,13 +54,13 @@
54
54
  "streamdown": "^2.5.0",
55
55
  "unist-util-visit": "^5.1.0",
56
56
  "yet-another-react-lightbox": "^3.32.2",
57
- "@mcp-b/design-tokens": "0.55.0"
57
+ "@mcp-b/design-tokens": "0.56.1"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@ai-sdk/react": "^3.0.249",
61
- "@cloudflare/codemode": "^0.5.2",
62
- "@cloudflare/shell": "^0.4.3",
63
- "@cloudflare/think": "^0.18.0",
61
+ "@cloudflare/codemode": "https://github.com/WebMCP-org/do-runtime/releases/download/rook-sdk-155b267477ca/cloudflare-codemode-0.5.2.tgz",
62
+ "@cloudflare/shell": "https://github.com/WebMCP-org/do-runtime/releases/download/rook-sdk-155b267477ca/cloudflare-shell-0.4.3.tgz",
63
+ "@cloudflare/think": "https://github.com/WebMCP-org/do-runtime/releases/download/rook-sdk-155b267477ca/cloudflare-think-0.18.0.tgz",
64
64
  "@cloudflare/workers-types": "^5.20260811.1",
65
65
  "@storybook/addon-a11y": "^10.5.7",
66
66
  "@storybook/addon-docs": "^10.5.7",
@@ -79,7 +79,7 @@
79
79
  "@vitest/browser-playwright": "^4.1.10",
80
80
  "@vitest/coverage-v8": "^4.1.10",
81
81
  "@vitest/mocker": "^4.1.10",
82
- "agents": "^0.23.0",
82
+ "agents": "https://github.com/WebMCP-org/do-runtime/releases/download/rook-sdk-155b267477ca/agents-0.23.0.tgz",
83
83
  "ai": "^6.0.247",
84
84
  "cloudflare": "^7.0.0",
85
85
  "oversight-lint": "0.7.1",
@@ -1,89 +0,0 @@
1
- import { ChatStatus, UIMessage } from "ai";
2
-
3
- //#region src/components/agents-sdk/SessionCompaction.d.ts
4
- type SessionStatusPhase = "idle" | "compacting";
5
- interface SessionStatusFrame {
6
- phase: SessionStatusPhase;
7
- /** Present on the idle frame that follows a successful compaction. */
8
- compacted: boolean;
9
- /**
10
- * Session's own estimate for the history this frame describes, or null when
11
- * the frame omits it or carries a non-finite value. Not provider-reported
12
- * usage — never render it as an exact token count, and never feed it to the
13
- * `Context` ring, which reads `metadata.usage`.
14
- */
15
- tokenEstimate: number | null;
16
- /**
17
- * The same estimate taken before the compaction this frame reports, from its
18
- * `compacted` payload. Null on every other frame. Both numbers come from one
19
- * estimator over one history, so their difference is a meaningful size even
20
- * though neither is exact.
21
- */
22
- tokensBefore: number | null;
23
- }
24
- /**
25
- * Parse a raw websocket payload as a `cf_agent_session` status frame.
26
- * Returns null for every other frame on the connection.
27
- */
28
- declare function parseSessionStatusFrame(data: unknown): SessionStatusFrame | null;
29
- /**
30
- * Parse a raw websocket payload as a `cf_agent_session_error` frame,
31
- * returning its error text. Null for every other frame on the connection.
32
- */
33
- declare function parseSessionErrorFrame(data: unknown): string | null;
34
- /** The message-frame surface of a `useAgent` connection. */
35
- interface SessionFrameSource {
36
- addEventListener(type: "message", listener: (event: MessageEvent) => void): void;
37
- removeEventListener(type: "message", listener: (event: MessageEvent) => void): void;
38
- }
39
- interface UseSessionCompactionOptions<MessageT extends UIMessage> {
40
- /** The conversation agent's `useAgent` connection. */
41
- agent: SessionFrameSource;
42
- /** The conversation's `useAgentChat` value (status + local view setter). */
43
- chat: {
44
- status: ChatStatus;
45
- setMessages: (messages: MessageT[]) => void;
46
- };
47
- /** Server read used to reload the transcript after a compaction lands. */
48
- getMessages: () => Promise<MessageT[]>;
49
- }
50
- /**
51
- * How much a landed compaction shortened the history, per Session's own
52
- * estimator. A UI may show the difference as an approximate size; it must not
53
- * present either number as exact usage.
54
- */
55
- interface SessionCompactionSize {
56
- tokensBefore: number;
57
- tokensAfter: number;
58
- }
59
- interface UseSessionCompactionResult {
60
- compacting: boolean;
61
- /**
62
- * Size of the compaction that landed most recently on this connection, or
63
- * null when none has landed or the frames carried no usable estimates. A new
64
- * object per compaction, so a consumer can dismiss one acknowledgement by
65
- * identity and still show the next. Cleared when the next compaction starts.
66
- */
67
- lastCompaction: SessionCompactionSize | null;
68
- /**
69
- * Set when the post-compaction transcript reload rejected — the local view
70
- * is stale relative to the server until a retry succeeds. Surface it
71
- * ("Transcript may be out of date"); the hook retries on the next status
72
- * transition to `"ready"`.
73
- */
74
- reloadError: unknown;
75
- /**
76
- * Error text from the last `cf_agent_session_error` frame — the only signal
77
- * a user who ran /compact gets when the server-side compaction itself fails
78
- * (the phase just flips back to idle with no `compacted` payload). Cleared
79
- * when the next compaction starts.
80
- */
81
- sessionError: string | null;
82
- }
83
- declare function useSessionCompaction<MessageT extends UIMessage>({
84
- agent,
85
- chat,
86
- getMessages
87
- }: UseSessionCompactionOptions<MessageT>): UseSessionCompactionResult;
88
- //#endregion
89
- export { UseSessionCompactionOptions as a, parseSessionStatusFrame as c, SessionStatusPhase as i, useSessionCompaction as l, SessionFrameSource as n, UseSessionCompactionResult as o, SessionStatusFrame as r, parseSessionErrorFrame as s, SessionCompactionSize as t };
@@ -1,2 +0,0 @@
1
- import { a as UseSessionCompactionOptions, c as parseSessionStatusFrame, i as SessionStatusPhase, l as useSessionCompaction, n as SessionFrameSource, o as UseSessionCompactionResult, r as SessionStatusFrame, s as parseSessionErrorFrame, t as SessionCompactionSize } from "../../SessionCompaction-CmTJFpzj.js";
2
- export { SessionCompactionSize, SessionFrameSource, SessionStatusFrame, SessionStatusPhase, UseSessionCompactionOptions, UseSessionCompactionResult, parseSessionErrorFrame, parseSessionStatusFrame, useSessionCompaction };
@@ -1,140 +0,0 @@
1
- import * as React from "react";
2
- //#region src/components/agents-sdk/SessionCompaction.ts
3
- /**
4
- * Client-side session-compaction plumbing for Think conversations.
5
- *
6
- * The Agents Session broadcasts `cf_agent_session` status frames to every
7
- * connected client: `phase` flips to `"compacting"` while a compaction runs
8
- * and back to `"idle"` when it settles, with a `compacted` payload on the
9
- * idle frame when an overlay was actually written. Think refreshes only its
10
- * server-side message cache after a compaction (`_syncMessages()` does not
11
- * push to clients), so a connected transcript keeps showing the uncompacted
12
- * history until its next `getMessages()`. `useSessionCompaction` closes that
13
- * gap: it tracks the live phase for a "Compacting context…" affordance and
14
- * reloads the transcript once a compaction lands — deferring the reload to
15
- * the end of the turn when one is streaming, so a mid-turn proactive
16
- * compaction never clobbers streamed state.
17
- *
18
- * Compose the command with `useAgentChatCompaction`: supply the app's
19
- * compaction callable, then place its command and notice explicitly.
20
- *
21
- * This module is the "local adapter … documented runtime-only mirror" that
22
- * docs/think-session-ui.md calls for: upstream exports no payload types or
23
- * client hook for these frames. The doc records the complete frame shapes;
24
- * this adapter consumes `phase`, the presence of `compacted`, the error string
25
- * of `cf_agent_session_error`, and the two estimates that size a landed
26
- * compaction (`tokenEstimate` and `compacted.tokensBefore`). Every field but
27
- * `phase` degrades to null rather than throwing, so an upstream field change
28
- * costs detail, never the UI. Per that doc, `tokenThreshold` is the
29
- * auto-compaction threshold, not the model context window — it stays unread
30
- * here, and neither estimate may drive the `Context` ring, which reads
31
- * provider-reported `metadata.usage`.
32
- *
33
- * @see ../../docs/think-session-ui.md
34
- * @see https://github.com/cloudflare/agents/blob/main/docs/agents/sessions.md#compaction
35
- */
36
- /** Finite numbers only — an absent or malformed field degrades to null. */
37
- function finiteNumber(value) {
38
- return typeof value === "number" && Number.isFinite(value) ? value : null;
39
- }
40
- /**
41
- * Parse a raw websocket payload as a `cf_agent_session` status frame.
42
- * Returns null for every other frame on the connection.
43
- */
44
- function parseSessionStatusFrame(data) {
45
- if (typeof data !== "string") return null;
46
- let message;
47
- try {
48
- message = JSON.parse(data);
49
- } catch {
50
- return null;
51
- }
52
- if (typeof message !== "object" || message === null || !("type" in message) || message.type !== "cf_agent_session" || !("phase" in message) || message.phase !== "idle" && message.phase !== "compacting") return null;
53
- const compacted = "compacted" in message && message.compacted != null ? message.compacted : null;
54
- return {
55
- phase: message.phase,
56
- compacted: compacted !== null,
57
- tokenEstimate: finiteNumber("tokenEstimate" in message ? message.tokenEstimate : void 0),
58
- tokensBefore: typeof compacted === "object" && compacted !== null && "tokensBefore" in compacted ? finiteNumber(compacted.tokensBefore) : null
59
- };
60
- }
61
- /**
62
- * Parse a raw websocket payload as a `cf_agent_session_error` frame,
63
- * returning its error text. Null for every other frame on the connection.
64
- */
65
- function parseSessionErrorFrame(data) {
66
- if (typeof data !== "string") return null;
67
- let message;
68
- try {
69
- message = JSON.parse(data);
70
- } catch {
71
- return null;
72
- }
73
- if (typeof message !== "object" || message === null || !("type" in message) || message.type !== "cf_agent_session_error" || !("error" in message) || typeof message.error !== "string") return null;
74
- return message.error;
75
- }
76
- function useSessionCompaction({ agent, chat, getMessages }) {
77
- const [compacting, setCompacting] = React.useState(false);
78
- const [refreshPending, setRefreshPending] = React.useState(false);
79
- const [reloadError, setReloadError] = React.useState(null);
80
- const [sessionError, setSessionError] = React.useState(null);
81
- const [lastCompaction, setLastCompaction] = React.useState(null);
82
- React.useEffect(() => {
83
- setCompacting(false);
84
- setSessionError(null);
85
- setRefreshPending(false);
86
- setReloadError(null);
87
- setLastCompaction(null);
88
- const onMessage = (event) => {
89
- const frame = parseSessionStatusFrame(event.data);
90
- if (frame) {
91
- setCompacting(frame.phase === "compacting");
92
- if (frame.phase === "compacting") {
93
- setSessionError(null);
94
- setLastCompaction(null);
95
- }
96
- if (frame.compacted) {
97
- setRefreshPending(true);
98
- setLastCompaction(frame.tokensBefore !== null && frame.tokenEstimate !== null ? {
99
- tokensAfter: frame.tokenEstimate,
100
- tokensBefore: frame.tokensBefore
101
- } : null);
102
- }
103
- return;
104
- }
105
- const error = parseSessionErrorFrame(event.data);
106
- if (error !== null) {
107
- setCompacting(false);
108
- setSessionError(error);
109
- }
110
- };
111
- agent.addEventListener("message", onMessage);
112
- return () => agent.removeEventListener("message", onMessage);
113
- }, [agent]);
114
- const { status, setMessages } = chat;
115
- React.useEffect(() => {
116
- if (!refreshPending || status !== "ready") return;
117
- let stale = false;
118
- getMessages().then((messages) => {
119
- if (stale) return;
120
- setRefreshPending(false);
121
- setReloadError(null);
122
- setMessages(messages);
123
- }, (error) => {
124
- if (stale) return;
125
- console.error("Transcript reload after compaction failed", error);
126
- setReloadError(error);
127
- });
128
- return () => {
129
- stale = true;
130
- };
131
- }, [refreshPending, status]);
132
- return {
133
- compacting,
134
- lastCompaction,
135
- reloadError,
136
- sessionError
137
- };
138
- }
139
- //#endregion
140
- export { parseSessionErrorFrame, parseSessionStatusFrame, useSessionCompaction };