@kortix/ui 0.14.4 → 0.14.5

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 (118) hide show
  1. package/README.md +8 -4
  2. package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
  3. package/dist/customize/sections/llm-provider/constants.js +0 -2
  4. package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
  5. package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
  6. package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
  7. package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
  8. package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
  9. package/dist/customize/sections/llm-provider/models-tab.js +1 -10
  10. package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
  11. package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
  12. package/dist/customize/sections/llm-provider/utils.js +7 -14
  13. package/dist/customize/sections/view/gateway/_shared.js +1 -2
  14. package/dist/customize/sections/view/gateway/gateway-routing.js +3 -2
  15. package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
  16. package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
  17. package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
  18. package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
  19. package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
  20. package/dist/customize/shared/entity/entity-modal.js +7 -4
  21. package/dist/customize/skills/remove-skill-button.d.ts +16 -0
  22. package/dist/customize/skills/remove-skill-button.js +42 -0
  23. package/dist/customize/skills/skills-page.js +1 -1
  24. package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
  25. package/dist/customize/triggers/schedule/trigger-list.js +1 -18
  26. package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
  27. package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
  28. package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
  29. package/dist/files/project/hooks/use-change-requests.js +44 -25
  30. package/dist/files/renderers/docx/docx-viewer.js +4 -1
  31. package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
  32. package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
  33. package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
  34. package/dist/files/renderers/pdf/pdf-viewer.js +15 -6
  35. package/dist/files/viewer/file-content-renderer.d.ts +4 -1
  36. package/dist/files/viewer/file-content-renderer.js +2 -2
  37. package/dist/i18n/en.d.ts +26 -35
  38. package/dist/i18n/en.js +35 -44
  39. package/dist/index.d.ts +1 -1
  40. package/dist/internal/hooks/use-notification-center.js +21 -15
  41. package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
  42. package/dist/internal/hooks/use-review-session-summary.js +11 -0
  43. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  44. package/dist/internal/lib/browser-error-noise.js +1 -1
  45. package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
  46. package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
  47. package/dist/internal/lib/provider-branding.js +14 -3
  48. package/dist/internal/lib/trigger-clock.d.ts +42 -0
  49. package/dist/internal/lib/trigger-clock.js +34 -0
  50. package/dist/internal/lib/web-notifications.d.ts +6 -0
  51. package/dist/internal/lib/web-notifications.js +57 -7
  52. package/dist/internal/notifications/notification-bell.d.ts +13 -0
  53. package/dist/internal/notifications/notification-bell.js +1 -1
  54. package/dist/internal/reminders/reminder-filter.d.ts +12 -0
  55. package/dist/internal/reminders/reminder-filter.js +34 -0
  56. package/dist/internal/reminders/reminder-format.d.ts +6 -0
  57. package/dist/internal/reminders/reminder-format.js +20 -2
  58. package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
  59. package/dist/internal/stores/session-hover-store.d.ts +20 -5
  60. package/dist/internal/stores/session-hover-store.js +31 -13
  61. package/dist/internal/stores/session-panel-memory-store.js +2 -0
  62. package/dist/primitives/count-badge.d.ts +3 -1
  63. package/dist/primitives/count-badge.js +3 -3
  64. package/dist/primitives/glyph-paths-source.js +1 -1
  65. package/dist/primitives/glyph-paths.generated.js +1 -1
  66. package/dist/providers/ui-provider.d.ts +57 -0
  67. package/dist/providers/ui-provider.js +4 -0
  68. package/dist/session/composer/ComposerCard.d.ts +2 -0
  69. package/dist/session/composer/ComposerCard.js +5 -1
  70. package/dist/session/composer/agent-selector.js +12 -5
  71. package/dist/session/composer/composer-toolbar.js +4 -2
  72. package/dist/session/composer/composer-underbar.js +3 -1
  73. package/dist/session/composer/composer.js +34 -5
  74. package/dist/session/composer/menus/slash-actions.d.ts +22 -2
  75. package/dist/session/composer/menus/slash-actions.js +20 -0
  76. package/dist/session/composer/menus/slash-items.js +19 -3
  77. package/dist/session/header/reminder-parts.d.ts +285 -0
  78. package/dist/session/header/reminder-parts.js +115 -0
  79. package/dist/session/header/session-reminders-indicator.d.ts +13 -0
  80. package/dist/session/header/session-reminders-indicator.js +142 -35
  81. package/dist/session/models/model-availability.d.ts +23 -0
  82. package/dist/session/models/model-availability.js +20 -0
  83. package/dist/session/models/model-grouping.js +1 -4
  84. package/dist/session/models/model-selector.js +23 -14
  85. package/dist/session/models/use-model-connection-gate.js +4 -1
  86. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  87. package/dist/session/outcomes/session-outcomes-provider.js +31 -3
  88. package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
  89. package/dist/session/overrides/session-overrides-composer.js +5 -1
  90. package/dist/session/panel/mobile-tool-drawer.js +4 -2
  91. package/dist/session/panel/open-session-quick-view.js +2 -1
  92. package/dist/session/panel/panel-fallback.js +8 -4
  93. package/dist/session/panel/session-panel-provider.d.ts +1 -0
  94. package/dist/session/panel/session-panel-provider.js +13 -1
  95. package/dist/session/panel/session-reminders-panel.d.ts +4 -0
  96. package/dist/session/panel/session-reminders-panel.js +109 -0
  97. package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
  98. package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
  99. package/dist/session/print/session-print-header.d.ts +0 -10
  100. package/dist/session/print/session-print-header.js +16 -1
  101. package/dist/session/status/session-surface.js +1 -1
  102. package/dist/session/thread/optimistic-turn.d.ts +5 -1
  103. package/dist/session/thread/optimistic-turn.js +8 -6
  104. package/dist/session/thread/session-chat.d.ts +6 -1
  105. package/dist/session/thread/session-chat.js +22 -3
  106. package/dist/session/thread/session-thread.d.ts +10 -1
  107. package/dist/session/thread/session-thread.js +2 -2
  108. package/dist/session/turn/burst-step-body.js +4 -0
  109. package/dist/styles.css +1 -1
  110. package/dist/workspace/sidebar/project-session-list.js +52 -55
  111. package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
  112. package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
  113. package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
  114. package/dist/workspace/sidebar/session-status-mark.js +63 -17
  115. package/package.json +7 -3
  116. package/skills/kortix-ui/SKILL.md +1 -1
  117. package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
  118. package/dist/customize/sections/view/sandbox-provider-result.js +0 -79
@@ -1,5 +1,19 @@
1
1
  import type { SessionDisplayStatus } from '../../session/actions/session-label.js';
2
+ /**
3
+ * A review is overdue when an open change request has not moved for 24h.
4
+ * The row then turns the review glyph from blue (your turn) to yellow (still
5
+ * waiting on you). Merged and closed requests never count.
6
+ */
7
+ export declare function isReviewOverdue(changeRequests: readonly {
8
+ status: string;
9
+ updated_at: string;
10
+ }[], now?: number): boolean;
2
11
  /** A status glyph with stable 16px geometry across every session state. */
3
- export declare function SessionStatusMark({ status }: {
12
+ export declare function SessionStatusMark({ status, waitingOn, overdue, }: {
4
13
  status: SessionDisplayStatus;
14
+ /** `needs-you` only: an agent question or permission (`answer`) or a change
15
+ * request (`review`). An answer blocks the agent, so callers pass it first. */
16
+ waitingOn?: 'answer' | 'review';
17
+ /** `needs-you` review only: it has waited too long, see `isReviewOverdue`. */
18
+ overdue?: boolean;
5
19
  }): import("react").JSX.Element;
@@ -1,24 +1,70 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import Loading from '../../primitives/loading.js';
4
- import { ClockCounterClockwiseIcon } from '@phosphor-icons/react';
5
- const STATUS_MARK_STYLE = {
6
- 'needs-you': { color: 'var(--kortix-green)', glyph: 'ring', fill: true },
7
- starting: { color: 'var(--kortix-yellow)', glyph: 'ring', fill: false },
8
- running: { color: 'var(--kortix-green)', glyph: 'ring', fill: true },
9
- done: { color: 'var(--muted-foreground)', glyph: 'check', fill: false },
10
- stopped: { color: 'var(--muted-foreground)', glyph: 'ring', fill: false },
11
- failed: { color: 'var(--kortix-red)', glyph: 'ring', fill: true },
12
- legacy: { color: 'var(--muted-foreground)', glyph: 'ring', fill: false },
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ClockCounterClockwiseIcon, GitDiffIcon, QuestionIcon } from '@phosphor-icons/react';
4
+ /**
5
+ * Color says who acts: gray nobody, green the agent, blue you (yellow once it waits
6
+ * over 24h), red broken.
7
+ * `starting` draws the stopped ring: a wake is meant to be instant, so it gets
8
+ * no state of its own, and blue and yellow stay reserved for `needs-you`.
9
+ */
10
+ const RING_STATUS_STYLE = {
11
+ starting: { color: 'var(--muted-foreground)', fill: false },
12
+ stopped: { color: 'var(--muted-foreground)', fill: false },
13
+ failed: { color: 'var(--kortix-red)', fill: true },
13
14
  };
15
+ const DOT_COUNT = 8;
16
+ const DOT_RADIUS = 6;
17
+ const DOTS = Array.from({ length: DOT_COUNT }, (_, i) => {
18
+ const angle = (i * 2 * Math.PI) / DOT_COUNT;
19
+ return { cx: 8 + DOT_RADIUS * Math.sin(angle), cy: 8 - DOT_RADIUS * Math.cos(angle) };
20
+ });
21
+ /** Behind the arc, clockwise: each dash fainter, so the still glyph reads as
22
+ * turning. Nothing animates: many rows can be working at once. */
23
+ const WORKING_TRAIL = [
24
+ { d: 'M2.84 4.39 A6.3 6.3 0 0 1 4.39 2.84', opacity: 0.85 },
25
+ { d: 'M1.8 9.1 A6.3 6.3 0 0 1 1.8 6.9', opacity: 0.55 },
26
+ { d: 'M4.39 13.16 A6.3 6.3 0 0 1 2.84 11.61', opacity: 0.25 },
27
+ ];
28
+ const GREEN_GLYPH = {
29
+ height: '16',
30
+ width: '16',
31
+ viewBox: '0 0 16 16',
32
+ style: { color: 'var(--kortix-green)' },
33
+ className: 'shrink-0',
34
+ 'aria-hidden': true,
35
+ };
36
+ /** `done` (agent idle, ready for a prompt): the dot ring filled with a core. */
37
+ function ReadyGlyph() {
38
+ return (_jsxs("svg", { ...GREEN_GLYPH, "data-glyph": "ready", children: [DOTS.map((dot, i) => (_jsx("circle", { "data-dot": "", cx: dot.cx, cy: dot.cy, r: "1", fill: "currentColor" }, i))), _jsx("circle", { "data-core": "", cx: "8", cy: "8", r: "3.6", fill: "currentColor" })] }));
39
+ }
40
+ /** `running`: a solid half arc leading a fading trail of dashes. */
41
+ function WorkingGlyph() {
42
+ return (_jsxs("svg", { ...GREEN_GLYPH, "data-glyph": "working", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [WORKING_TRAIL.map((dash) => (_jsx("path", { "data-dash": "", d: dash.d, opacity: dash.opacity }, dash.d))), _jsx("path", { d: "M8 1.7 A6.3 6.3 0 0 1 8 14.3" })] }));
43
+ }
44
+ const REVIEW_OVERDUE_MS = 24 * 60 * 60 * 1000;
45
+ /**
46
+ * A review is overdue when an open change request has not moved for 24h.
47
+ * The row then turns the review glyph from blue (your turn) to yellow (still
48
+ * waiting on you). Merged and closed requests never count.
49
+ */
50
+ export function isReviewOverdue(changeRequests, now = Date.now()) {
51
+ return changeRequests.some((cr) => cr.status === 'open' && now - Date.parse(cr.updated_at) >= REVIEW_OVERDUE_MS);
52
+ }
14
53
  /** A status glyph with stable 16px geometry across every session state. */
15
- export function SessionStatusMark({ status }) {
16
- const style = STATUS_MARK_STYLE[status];
17
- if (status === 'starting') {
18
- return _jsx(Loading, { className: "text-kortix-yellow size-3.5", "aria-hidden": true });
54
+ export function SessionStatusMark({ status, waitingOn = 'review', overdue = false, }) {
55
+ if (status === 'done')
56
+ return _jsx(ReadyGlyph, {});
57
+ if (status === 'running')
58
+ return _jsx(WorkingGlyph, {});
59
+ if (status === 'needs-you' && waitingOn === 'answer') {
60
+ return (_jsx(QuestionIcon, { "data-glyph": "question", className: "size-4 shrink-0", style: { color: 'var(--kortix-blue)' }, "aria-hidden": true }));
61
+ }
62
+ if (status === 'needs-you') {
63
+ return (_jsx(GitDiffIcon, { "data-glyph": "review", className: "size-4 shrink-0", style: { color: overdue ? 'var(--kortix-yellow)' : 'var(--kortix-blue)' }, "aria-hidden": true }));
19
64
  }
20
65
  if (status === 'legacy') {
21
- return (_jsx(ClockCounterClockwiseIcon, { className: "size-3.5 shrink-0", style: { color: style.color }, "aria-hidden": true }));
66
+ return (_jsx(ClockCounterClockwiseIcon, { className: "size-3.5 shrink-0", style: { color: 'var(--muted-foreground)' }, "aria-hidden": true }));
22
67
  }
23
- return (_jsx("svg", { height: "16", width: "16", viewBox: "0 0 16 16", strokeLinejoin: "round", style: { color: style.color }, className: "shrink-0", "aria-hidden": true, children: style.glyph === 'check' ? (_jsx("path", { d: "M4 8.4 L6.8 11.2 L12 5.2", stroke: "currentColor", fill: "none", strokeWidth: "1.5", strokeLinecap: "round" })) : (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "8", cy: "8", r: "6.3", stroke: "currentColor", fill: "none", strokeWidth: "1.5" }), style.fill && (_jsx("circle", { cx: "8", cy: "8", r: status === 'needs-you' ? 3.2 : 4, fill: "currentColor" }))] })) }));
68
+ const style = RING_STATUS_STYLE[status];
69
+ return (_jsxs("svg", { height: "16", width: "16", viewBox: "0 0 16 16", style: { color: style.color }, className: "shrink-0", "aria-hidden": true, children: [_jsx("circle", { cx: "8", cy: "8", r: "6.3", stroke: "currentColor", fill: "none", strokeWidth: "1.5" }), style.fill && _jsx("circle", { cx: "8", cy: "8", r: "4", fill: "currentColor" })] }));
24
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kortix/ui",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "description": "Kortix UI: the shared React component library for apps/web, Kortix Apps and SDK hosts. Tailwind v4 classes, themed by CSS variables.",
5
5
  "type": "module",
6
6
  "license": "Elastic-2.0",
@@ -1637,6 +1637,10 @@
1637
1637
  "types": "./dist/session/models/model-grouping.d.ts",
1638
1638
  "import": "./dist/session/models/model-grouping.js"
1639
1639
  },
1640
+ "./session/models/model-search-empty": {
1641
+ "types": "./dist/session/models/model-search-empty.d.ts",
1642
+ "import": "./dist/session/models/model-search-empty.js"
1643
+ },
1640
1644
  "./session/models/model-selector": {
1641
1645
  "types": "./dist/session/models/model-selector.d.ts",
1642
1646
  "import": "./dist/session/models/model-selector.js"
@@ -2153,7 +2157,7 @@
2153
2157
  "@extend-ai/react-docx": "^0.7.5",
2154
2158
  "@extend-ai/react-xlsx": "0.13.4",
2155
2159
  "@glideapps/glide-data-grid": "6.0.4-alpha24",
2156
- "@kortix/llm-catalog": "0.14.4",
2160
+ "@kortix/llm-catalog": "0.14.5",
2157
2161
  "@lezer/highlight": "1.2.3",
2158
2162
  "@paper-design/shaders-react": "^0.0.76",
2159
2163
  "@phosphor-icons/react": "2.1.10",
@@ -2242,7 +2246,7 @@
2242
2246
  "zustand": "^5.0.3"
2243
2247
  },
2244
2248
  "peerDependencies": {
2245
- "@kortix/sdk": "^0.14.4",
2249
+ "@kortix/sdk": "^0.14.5",
2246
2250
  "@tanstack/react-query": "^5.75.2",
2247
2251
  "@types/react": ">=18",
2248
2252
  "react": ">=19",
@@ -84,7 +84,7 @@ Theme with `@kortix/ui/theme`, never by forking a component: `themeToCss(theme)`
84
84
 
85
85
  | Import from | Names |
86
86
  | --- | --- |
87
- | `@kortix/ui` (adapters) | `cn`, `UiProvider`, `defaultUiStrings`, `useUiStrings`, `useUiLink`, `useUiImage`, `useUiBrand`, `UiStrings`, `UiStringsInput`, `UiLinkProps`, `UiLinkComponent`, `UiImageProps`, `UiImageComponent`, `UiBrand`, `UseUiBrand`, `useIsDark`, `useUiRouter`, `useUiShallowNavigate`, `UiShallowNavigate`, `useUiPathname`, `useUiSearchParams`, `useUiParams`, `defaultUiNavigation`, `UiNavigation`, `UiRouter`, `UiRouteParams`, `UiSearchParams`, `useUiViewerId`, `useUiHostConfig`, `defaultUiHostConfig`, `UiHostConfig`, `SessionIdsProvider`, `useSessionIds`, `UiSessionIds` |
87
+ | `@kortix/ui` (adapters) | `cn`, `UiProvider`, `defaultUiStrings`, `useUiStrings`, `useUiLink`, `useUiImage`, `useUiBrand`, `UiStrings`, `UiStringsInput`, `UiLinkProps`, `UiLinkComponent`, `UiImageProps`, `UiImageComponent`, `UiBrand`, `UseUiBrand`, `useIsDark`, `useUiRouter`, `useUiShallowNavigate`, `UiShallowNavigate`, `useUiPathname`, `useUiSearchParams`, `useUiParams`, `defaultUiNavigation`, `UiNavigation`, `UiRouter`, `UiRouteParams`, `UiSearchParams`, `useUiViewerId`, `useUiHostConfig`, `defaultUiHostConfig`, `UiHostConfig`, `UiSlashAction`, `SessionIdsProvider`, `useSessionIds`, `UiSessionIds` |
88
88
  | `@kortix/ui` (actions, display) | `Button`, `buttonVariants`, `ButtonProps`, `Badge`, `badgeVariants`, `Card`, `CardAction`, `CardButton`, `CardContent`, `CardDescription`, `CardEyebrow`, `CardFeature`, `CardFooter`, `CardGroup`, `CardHeader`, `CardImage`, `CardMedia`, `CardTitle`, `CardButtonProps`, `CardButtonSize`, `CardButtonVariant`, `CardGroupProps`, `CardLogo`, `CardProps`, `Separator`, `Skeleton` |
89
89
  | `@kortix/ui` (forms) | `Input`, `inputFocusClasses`, `inputInvalidClasses`, `inputSurfaceClasses`, `inputTransitionClasses`, `InputProps`, `useAutosizeTextArea`, `Textarea`, `AutosizeTextAreaRef`, `AutosizeTextAreaProps`, `Label`, `Checkbox`, `CheckboxProps`, `CheckboxVariant`, `Switch`, `SwitchProps`, `Select`, `SelectContent`, `SelectGroup`, `SelectItem`, `SelectLabel`, `SelectScrollDownButton`, `SelectScrollUpButton`, `SelectSeparator`, `SelectTrigger`, `SelectValue`, `SelectTriggerProps` |
90
90
  | `@kortix/ui` (overlays) | `Modal`, `ModalBody`, `ModalClose`, `ModalContent`, `ModalContentInner`, `ModalDescription`, `ModalFooter`, `ModalHeader`, `ModalOverlay`, `ModalPortal`, `ModalTitle`, `ModalTrigger`, `Hint`, `HintProps`, `Popover`, `PopoverAnchor`, `PopoverContent`, `PopoverTrigger`, `DropdownMenu`, `DropdownMenuCheckboxItem`, `DropdownMenuContent`, `DropdownMenuGroup`, `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuPortal`, `DropdownMenuRadioGroup`, `DropdownMenuRadioItem`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, `DropdownMenuSub`, `DropdownMenuSubContent`, `DropdownMenuSubTrigger`, `DropdownMenuTrigger` |
@@ -1,47 +0,0 @@
1
- import { type SandboxProviderTransitionState, type UpdateProjectSandboxProviderResult } from '@kortix/sdk';
2
- import type { QueryClient } from '@tanstack/react-query';
3
- /**
4
- * A provider-migration transition never changes again once it reaches one of
5
- * these statuses (mirrors the API's TERMINAL_TRANSITION_STATUSES) — plus the
6
- * immediate `noop`/`cleared` markers a transition row can carry. Polling stops
7
- * on any of them.
8
- */
9
- export declare const SANDBOX_PROVIDER_TERMINAL_STATUSES: Set<string>;
10
- /** A missing/absent status (no live transition) is treated as terminal too. */
11
- export declare function isSandboxProviderTransitionTerminal(status: string | null | undefined): boolean;
12
- type CacheClient = Pick<QueryClient, 'setQueryData' | 'invalidateQueries'>;
13
- /**
14
- * FIX-L: apply the PATCH /sandbox-provider result to the query cache.
15
- *
16
- * Writes the project caches ONLY for the immediate `kind:'project'` result. A
17
- * `kind:'preparation'` result is a durable TRANSITION object, not a project —
18
- * writing it into `qk.project.summary(id)` would corrupt the cached project
19
- * shape (it has no repo_url / metadata / experimental_features …). On
20
- * preparation we leave the project cache untouched and return `'preparation'`
21
- * so the caller polls the transition instead. Returns the result's kind.
22
- */
23
- export declare function applySandboxProviderResult(queryClient: CacheClient, projectId: string, result: UpdateProjectSandboxProviderResult): 'project' | 'preparation';
24
- export interface PollSandboxProviderTransitionOptions {
25
- /** Injected for tests; defaults to the SDK poll call. */
26
- fetchState?: (projectId: string) => Promise<SandboxProviderTransitionState>;
27
- /** Called once when polling stops (terminal status, no transition, or exhausted).
28
- * `null` means the poll ended without a readable state (404/no-transition). */
29
- onSettled?: (state: SandboxProviderTransitionState | null) => void;
30
- maxAttempts?: number;
31
- baseDelayMs?: number;
32
- maxDelayMs?: number;
33
- /** Cooperative cancellation (e.g. component unmounted). */
34
- signal?: {
35
- aborted: boolean;
36
- };
37
- /** Injected for tests; defaults to setTimeout. */
38
- sleep?: (ms: number) => Promise<void>;
39
- }
40
- /**
41
- * FIX-L: poll the durable provider-migration transition after a `kind:'preparation'`
42
- * switch. Bounded (maxAttempts) with exponential backoff; stops on a terminal
43
- * status, and treats a 404 / no-transition / any read error as terminal (nothing
44
- * left to poll). Never throws — surfaces progress via `onSettled`.
45
- */
46
- export declare function pollSandboxProviderTransition(projectId: string, opts?: PollSandboxProviderTransitionOptions): Promise<SandboxProviderTransitionState | null>;
47
- export {};
@@ -1,79 +0,0 @@
1
- import { getProjectSandboxProviderTransition, } from '@kortix/sdk';
2
- import { qk } from '@kortix/sdk/react';
3
- /**
4
- * A provider-migration transition never changes again once it reaches one of
5
- * these statuses (mirrors the API's TERMINAL_TRANSITION_STATUSES) — plus the
6
- * immediate `noop`/`cleared` markers a transition row can carry. Polling stops
7
- * on any of them.
8
- */
9
- export const SANDBOX_PROVIDER_TERMINAL_STATUSES = new Set([
10
- 'activated',
11
- 'failed',
12
- 'superseded',
13
- 'cancelled',
14
- 'noop',
15
- 'cleared',
16
- ]);
17
- /** A missing/absent status (no live transition) is treated as terminal too. */
18
- export function isSandboxProviderTransitionTerminal(status) {
19
- return status == null || SANDBOX_PROVIDER_TERMINAL_STATUSES.has(status);
20
- }
21
- /**
22
- * FIX-L: apply the PATCH /sandbox-provider result to the query cache.
23
- *
24
- * Writes the project caches ONLY for the immediate `kind:'project'` result. A
25
- * `kind:'preparation'` result is a durable TRANSITION object, not a project —
26
- * writing it into `qk.project.summary(id)` would corrupt the cached project
27
- * shape (it has no repo_url / metadata / experimental_features …). On
28
- * preparation we leave the project cache untouched and return `'preparation'`
29
- * so the caller polls the transition instead. Returns the result's kind.
30
- */
31
- export function applySandboxProviderResult(queryClient, projectId, result) {
32
- if (result.kind !== 'project')
33
- return 'preparation';
34
- // Strip the discriminant so the cached value is a pure KortixProject.
35
- const { kind: _kind, ...project } = result;
36
- const cached = project;
37
- queryClient.setQueryData(qk.project.summary(projectId), cached);
38
- queryClient.setQueryData(qk.project.detail(projectId), (c) => c ? { ...c, project: cached } : c);
39
- queryClient.invalidateQueries({ queryKey: qk.project.detail(projectId) });
40
- // qk.projects.scope(), not the precise per-account form: restores the
41
- // reach the old bare projects-literal prefix match had — every account's
42
- // list, and the accountless slot the marketplace picker reads. A
43
- // sandbox-provider switch is rare — over-invalidating costs nothing.
44
- queryClient.invalidateQueries({ queryKey: qk.projects.scope() });
45
- return 'project';
46
- }
47
- /**
48
- * FIX-L: poll the durable provider-migration transition after a `kind:'preparation'`
49
- * switch. Bounded (maxAttempts) with exponential backoff; stops on a terminal
50
- * status, and treats a 404 / no-transition / any read error as terminal (nothing
51
- * left to poll). Never throws — surfaces progress via `onSettled`.
52
- */
53
- export async function pollSandboxProviderTransition(projectId, opts = {}) {
54
- const fetchState = opts.fetchState ?? getProjectSandboxProviderTransition;
55
- const maxAttempts = opts.maxAttempts ?? 60;
56
- const baseDelayMs = opts.baseDelayMs ?? 2_000;
57
- const maxDelayMs = opts.maxDelayMs ?? 15_000;
58
- const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
59
- let last = null;
60
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
61
- if (opts.signal?.aborted)
62
- return last;
63
- try {
64
- last = await fetchState(projectId);
65
- }
66
- catch {
67
- // 404 / no transition / transient read failure → nothing to keep polling.
68
- opts.onSettled?.(null);
69
- return null;
70
- }
71
- if (isSandboxProviderTransitionTerminal(last?.latest?.status)) {
72
- opts.onSettled?.(last);
73
- return last;
74
- }
75
- await sleep(Math.min(baseDelayMs * 2 ** attempt, maxDelayMs));
76
- }
77
- opts.onSettled?.(last);
78
- return last;
79
- }