basalt-ui 1.9.0 → 1.11.0
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/AGENTS.md +2 -1
- package/CHANGELOG.md +18 -0
- package/README.md +64 -34
- package/agent/rules/basalt-agent.md +49 -6
- package/configs/oxlint-plugin.js +279 -0
- package/configs/oxlint-plugin.test.ts +216 -1
- package/configs/oxlint.json +5 -2
- package/dist/agent/ai-sdk-transport.d.ts +3 -52
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +108 -24
- package/dist/agent/coalesce.d.ts +26 -0
- package/dist/agent/coalesce.d.ts.map +1 -0
- package/dist/agent/coalesce.js +69 -0
- package/dist/agent/foreign.d.ts +97 -0
- package/dist/agent/foreign.d.ts.map +1 -0
- package/dist/agent/foreign.js +18 -0
- package/dist/agent/history.d.ts +2 -0
- package/dist/agent/history.d.ts.map +1 -1
- package/dist/agent/id.d.ts +35 -0
- package/dist/agent/id.d.ts.map +1 -0
- package/dist/agent/id.js +14 -0
- package/dist/agent/index.d.ts +9 -4
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +15 -3
- package/dist/agent/merge.d.ts +8 -0
- package/dist/agent/merge.d.ts.map +1 -0
- package/dist/agent/merge.js +23 -0
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/part-list.js +16 -6
- package/dist/agent/parts.d.ts +100 -21
- package/dist/agent/parts.d.ts.map +1 -1
- package/dist/agent/parts.js +128 -11
- package/dist/agent/transport.d.ts +39 -3
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/transport.js +20 -17
- package/dist/agent/use-agent-stream.d.ts +2 -1
- package/dist/agent/use-agent-stream.d.ts.map +1 -1
- package/dist/agent/use-agent-stream.js +5 -2
- package/dist/agent/use-agent-thread-runs.d.ts +9 -3
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +76 -23
- package/dist/agent-chat/index.d.ts +6 -3
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/agent-chat/index.js +2 -0
- package/dist/agent-chat/thread-message.d.ts +24 -7
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +102 -43
- package/dist/agent-chat/tool-chip.d.ts +21 -0
- package/dist/agent-chat/tool-chip.d.ts.map +1 -0
- package/dist/agent-chat/tool-chip.js +120 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +131 -0
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +1 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/surfaces.d.ts +11 -1
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +22 -1
- package/llms.txt +10 -3
- package/package.json +6 -1
- package/src/agent/ai-sdk-transport.test.ts +567 -0
- package/src/agent/ai-sdk-transport.ts +206 -53
- package/src/agent/coalesce.test.ts +194 -0
- package/src/agent/coalesce.ts +118 -0
- package/src/agent/foreign.test.ts +47 -0
- package/src/agent/foreign.ts +140 -0
- package/src/agent/history.ts +2 -0
- package/src/agent/id.test.ts +71 -0
- package/src/agent/id.ts +43 -0
- package/src/agent/index.ts +26 -3
- package/src/agent/merge.test.ts +119 -0
- package/src/agent/merge.ts +67 -0
- package/src/agent/part-list.test.tsx +82 -0
- package/src/agent/part-list.tsx +31 -6
- package/src/agent/parts.test.ts +603 -0
- package/src/agent/parts.ts +262 -32
- package/src/agent/thread.test.ts +147 -0
- package/src/agent/transport.test.ts +70 -0
- package/src/agent/transport.ts +72 -20
- package/src/agent/use-agent-stream.test.tsx +261 -0
- package/src/agent/use-agent-stream.ts +13 -3
- package/src/agent/use-agent-thread-runs.foreign.test.tsx +133 -0
- package/src/agent/use-agent-thread-runs.resume.test.tsx +289 -0
- package/src/agent/use-agent-thread-runs.test.tsx +781 -0
- package/src/agent/use-agent-thread-runs.ts +177 -27
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +301 -0
- package/src/agent-chat/index.ts +8 -3
- package/src/agent-chat/thread-message.test.tsx +211 -0
- package/src/agent-chat/thread-message.tsx +235 -62
- package/src/agent-chat/tool-chip.test.tsx +139 -0
- package/src/agent-chat/tool-chip.tsx +219 -0
- package/src/charts/primitives/ChartFrame.test.tsx +8 -5
- package/src/cli/index.ts +285 -0
- package/src/dashboard/stat-card.test.tsx +5 -3
- package/src/guard/check-source.test.ts +8 -4
- package/src/guard/index.ts +1 -3
- package/src/index.ts +2 -0
- package/src/provider/index.test.tsx +6 -5
- package/src/surfaces.ts +23 -1
- package/src/theme/use-basalt-spacing.test.tsx +7 -4
- package/src/tokens/build-fonts-css.test.ts +3 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSX } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AgentPartRenderers, ChatMessage, ForeignPart, PartRenderer, PartRenderers, StreamStatus, TranscriptPart } from '../agent/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Mantine overrides for `PartList`'s default headless renderers — the house style for rendering
|
|
5
5
|
* an `AgentPart` inside a chat surface.
|
|
@@ -10,22 +10,39 @@ import type { AgentPart, AgentPartRenderers, ChatMessage, StreamStatus } from '.
|
|
|
10
10
|
* <PartList parts={parts} components={threadPartRenderers} />
|
|
11
11
|
*/
|
|
12
12
|
export declare const threadPartRenderers: Partial<AgentPartRenderers>;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Render-execution counter for `MessageBlockImpl` — increments only when React actually calls the
|
|
15
|
+
* function body (a `memo` bail-out never reaches it). NOT part of the public surface —
|
|
16
|
+
* `agent-chat/index.ts` does not re-export it. The memoization test imports this module directly
|
|
17
|
+
* (bypassing the barrel) to prove the memo boundary in a controlled render-count harness.
|
|
18
|
+
*/
|
|
19
|
+
export declare const messageBlockRenderCounter: {
|
|
20
|
+
count: number;
|
|
21
|
+
};
|
|
22
|
+
type ThreadTranscriptBase = {
|
|
14
23
|
/** Settled messages for the thread, oldest first. */
|
|
15
|
-
readonly messages: ChatMessage[];
|
|
24
|
+
readonly messages: readonly ChatMessage<TranscriptPart>[];
|
|
16
25
|
/** The live (in-flight) assistant turn's parts, when a run is streaming for this thread. */
|
|
17
|
-
readonly liveParts?:
|
|
26
|
+
readonly liveParts?: readonly TranscriptPart[];
|
|
18
27
|
/** The live run's status — drives the in-progress indicator on the live block. */
|
|
19
28
|
readonly liveStatus?: StreamStatus;
|
|
29
|
+
/** Consumer renderers keyed by part.type. Consulted BEFORE the built-in union. */
|
|
30
|
+
readonly renderers?: PartRenderers;
|
|
31
|
+
/** Called for a part whose type is neither an AgentPart variant nor a registered key. Defaults
|
|
32
|
+
* to a visible `UnknownPartChip` outside production, `null` in production — never throws. */
|
|
33
|
+
readonly fallbackRenderer?: PartRenderer<ForeignPart>;
|
|
20
34
|
};
|
|
35
|
+
export type ThreadTranscriptProps = ThreadTranscriptBase;
|
|
21
36
|
/**
|
|
22
|
-
* Renders a thread's settled messages, each role-labelled ("You" / "Assistant")
|
|
23
|
-
*
|
|
37
|
+
* Renders a thread's settled messages, each role-labelled ("You" / "Assistant"). Each part
|
|
38
|
+
* resolves in order: a registered `renderers[part.type]` first, then basalt's own `PartList` for
|
|
39
|
+
* the built-in six, then `fallbackRenderer`. When `liveParts` is non-empty, an extra in-progress
|
|
24
40
|
* assistant block is appended at the tail.
|
|
25
41
|
*
|
|
26
42
|
* @example
|
|
27
43
|
* import { ThreadTranscript } from 'basalt-ui'
|
|
28
44
|
* <ThreadTranscript messages={thread.messages} liveParts={liveParts} liveStatus={runStatus} />
|
|
29
45
|
*/
|
|
30
|
-
export declare function ThreadTranscript({ messages, liveParts, liveStatus, }: ThreadTranscriptProps): JSX.Element;
|
|
46
|
+
export declare function ThreadTranscript({ messages, liveParts, liveStatus, renderers: renderersProp, fallbackRenderer, }: ThreadTranscriptProps): JSX.Element;
|
|
47
|
+
export {};
|
|
31
48
|
//# sourceMappingURL=thread-message.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread-message.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-message.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"thread-message.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-message.tsx"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,KAAK,EAEV,kBAAkB,EAClB,WAAW,EAEX,WAAW,EACX,YAAY,EACZ,aAAa,EAGb,YAAY,EAGZ,cAAc,EACf,MAAM,UAAU,CAAA;AAkFjB;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,kBAAkB,CAM3D,CAAA;AAqID;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;CAAe,CAAA;AA8ErD,KAAK,oBAAoB,GAAG;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,CAAC,cAAc,CAAC,EAAE,CAAA;IACzD,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,cAAc,EAAE,CAAA;IAC9C,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,YAAY,CAAA;IAClC,kFAAkF;IAClF,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC;iGAC6F;IAC7F,QAAQ,CAAC,gBAAgB,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,CAAA;CACtD,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,CAAA;AAExD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,EAAE,aAAa,EACxB,gBAA4C,GAC7C,EAAE,qBAAqB,GAAG,GAAG,CAAC,OAAO,CAgCrC"}
|
|
@@ -11,9 +11,11 @@ import {
|
|
|
11
11
|
UnstyledButton
|
|
12
12
|
} from "@mantine/core";
|
|
13
13
|
import { useDisclosure } from "@mantine/hooks";
|
|
14
|
-
import {
|
|
14
|
+
import { Fragment, memo, useMemo } from "react";
|
|
15
|
+
import { coalesceParts, narrowAgentPart, PartList } from "../agent/index.js";
|
|
15
16
|
import { Markdown } from "../content/markdown.js";
|
|
16
|
-
import {
|
|
17
|
+
import { VX } from "../tokens/index.js";
|
|
18
|
+
import { ToolChip } from "./tool-chip.js";
|
|
17
19
|
const MICRO_LABEL_STYLE = {
|
|
18
20
|
fontFamily: "var(--basalt-font-mono)",
|
|
19
21
|
fontSize: VX.text.micro,
|
|
@@ -29,16 +31,6 @@ const RAIL_STYLE = {
|
|
|
29
31
|
borderLeft: `2px solid ${VX.divider}`,
|
|
30
32
|
paddingLeft: "var(--vx-space-agent-rail-inset-x)"
|
|
31
33
|
};
|
|
32
|
-
const CODE_BLOCK_STYLE = {
|
|
33
|
-
margin: 0,
|
|
34
|
-
marginTop: "var(--vx-space-agent-part-gap-top)",
|
|
35
|
-
fontFamily: "var(--basalt-font-mono)",
|
|
36
|
-
backgroundColor: alpha(VX.ink, 0.06),
|
|
37
|
-
borderRadius: "var(--vx-radius-card)",
|
|
38
|
-
padding: "var(--vx-space-agent-code-inset)",
|
|
39
|
-
whiteSpace: "pre-wrap",
|
|
40
|
-
overflowWrap: "anywhere"
|
|
41
|
-
};
|
|
42
34
|
function ReasoningRenderer({ part }) {
|
|
43
35
|
const [open, { toggle }] = useDisclosure(false);
|
|
44
36
|
return /* @__PURE__ */ jsx(Box, { style: RAIL_STYLE, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
@@ -46,12 +38,8 @@ function ReasoningRenderer({ part }) {
|
|
|
46
38
|
/* @__PURE__ */ jsx(Collapse, { expanded: open, children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { whiteSpace: "pre-wrap" }, children: part.text }) })
|
|
47
39
|
] }) });
|
|
48
40
|
}
|
|
49
|
-
function
|
|
50
|
-
return /* @__PURE__ */ jsx(
|
|
51
|
-
/* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: part.toolName }),
|
|
52
|
-
/* @__PURE__ */ jsx(Text, { component: "pre", size: "xs", style: CODE_BLOCK_STYLE, children: JSON.stringify(part.input, null, 2) }),
|
|
53
|
-
part.output !== void 0 && /* @__PURE__ */ jsx(Text, { component: "pre", size: "xs", style: CODE_BLOCK_STYLE, children: JSON.stringify(part.output, null, 2) })
|
|
54
|
-
] }) });
|
|
41
|
+
function ToolChipRenderer({ part }) {
|
|
42
|
+
return /* @__PURE__ */ jsx(ToolChip, { part });
|
|
55
43
|
}
|
|
56
44
|
function SourceRenderer({ part }) {
|
|
57
45
|
return /* @__PURE__ */ jsx(Anchor, { href: part.url, target: "_blank", rel: "noopener noreferrer", size: "sm", children: part.title ?? part.url });
|
|
@@ -62,70 +50,141 @@ function ErrorRenderer({ part }) {
|
|
|
62
50
|
const threadPartRenderers = {
|
|
63
51
|
text: TextRenderer,
|
|
64
52
|
reasoning: ReasoningRenderer,
|
|
65
|
-
tool:
|
|
53
|
+
tool: ToolChipRenderer,
|
|
66
54
|
source: SourceRenderer,
|
|
67
55
|
error: ErrorRenderer
|
|
68
56
|
};
|
|
69
|
-
function
|
|
70
|
-
|
|
57
|
+
function UnknownPartChip({ part }) {
|
|
58
|
+
return /* @__PURE__ */ jsx(Box, { style: RAIL_STYLE, children: /* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: `Unknown part: ${part.type}` }) });
|
|
59
|
+
}
|
|
60
|
+
const DEFAULT_FALLBACK_RENDERER = ({ part }) => process.env["NODE_ENV"] !== "production" ? /* @__PURE__ */ jsx(UnknownPartChip, { part }) : null;
|
|
61
|
+
function resolveSegments(parts, renderers, fallbackRenderer) {
|
|
62
|
+
const segments = [];
|
|
63
|
+
let buffer = [];
|
|
64
|
+
const flushBuffer = () => {
|
|
65
|
+
const first = buffer[0];
|
|
66
|
+
if (first === void 0) return;
|
|
67
|
+
segments.push({ kind: "agent", key: first.id, parts: coalesceParts(buffer) });
|
|
68
|
+
buffer = [];
|
|
69
|
+
};
|
|
71
70
|
for (const part of parts) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
const consumerRenderer = renderers[part.type];
|
|
72
|
+
if (consumerRenderer !== void 0) {
|
|
73
|
+
flushBuffer();
|
|
74
|
+
segments.push({
|
|
75
|
+
kind: "foreign",
|
|
76
|
+
key: part.id,
|
|
77
|
+
part,
|
|
78
|
+
render: consumerRenderer
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const agentPart = narrowAgentPart(part);
|
|
83
|
+
if (agentPart !== null) {
|
|
84
|
+
buffer.push(agentPart);
|
|
85
|
+
continue;
|
|
79
86
|
}
|
|
87
|
+
flushBuffer();
|
|
88
|
+
segments.push({
|
|
89
|
+
kind: "foreign",
|
|
90
|
+
key: part.id,
|
|
91
|
+
part,
|
|
92
|
+
render: fallbackRenderer
|
|
93
|
+
});
|
|
80
94
|
}
|
|
81
|
-
|
|
95
|
+
flushBuffer();
|
|
96
|
+
return segments;
|
|
82
97
|
}
|
|
83
98
|
const ROLE_LABEL = {
|
|
84
99
|
user: "You",
|
|
85
100
|
assistant: "Assistant"
|
|
86
101
|
};
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
const FINISH_INDICATOR = {
|
|
103
|
+
stopped: { label: "Stopped", statusToken: VX.status.warn },
|
|
104
|
+
error: { label: "Error", statusToken: VX.status.bad }
|
|
105
|
+
};
|
|
106
|
+
const messageBlockRenderCounter = { count: 0 };
|
|
107
|
+
function MessageBlockImpl({
|
|
108
|
+
message,
|
|
109
|
+
streaming = false,
|
|
110
|
+
renderers,
|
|
111
|
+
fallbackRenderer
|
|
91
112
|
}) {
|
|
113
|
+
messageBlockRenderCounter.count += 1;
|
|
114
|
+
const settled = !streaming;
|
|
115
|
+
const segments = useMemo(
|
|
116
|
+
() => resolveSegments(message.parts, renderers, fallbackRenderer),
|
|
117
|
+
[message.parts, renderers, fallbackRenderer]
|
|
118
|
+
);
|
|
119
|
+
const finishIndicator = message.finish === void 0 ? void 0 : FINISH_INDICATOR[message.finish];
|
|
92
120
|
return /* @__PURE__ */ jsx(
|
|
93
121
|
Box,
|
|
94
122
|
{
|
|
95
123
|
style: {
|
|
96
124
|
padding: "var(--vx-space-agent-message-inset-y) var(--vx-space-agent-message-inset-x)",
|
|
97
|
-
backgroundColor:
|
|
125
|
+
backgroundColor: message.role === "user" ? VX.surface.subtle : VX.surface.panel,
|
|
98
126
|
borderRadius: VX.radiusCard,
|
|
99
127
|
boxShadow: VX.shadowCard
|
|
100
128
|
},
|
|
101
129
|
children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
102
130
|
/* @__PURE__ */ jsxs(Group, { gap: 6, align: "center", children: [
|
|
103
|
-
/* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: ROLE_LABEL[
|
|
104
|
-
streaming && /* @__PURE__ */ jsx(Loader, { size: "xs" })
|
|
131
|
+
/* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: ROLE_LABEL[message.role] }),
|
|
132
|
+
streaming && /* @__PURE__ */ jsx(Loader, { size: "xs" }),
|
|
133
|
+
finishIndicator !== void 0 && /* @__PURE__ */ jsx(Text, { style: { ...MICRO_LABEL_STYLE, color: finishIndicator.statusToken }, children: finishIndicator.label })
|
|
105
134
|
] }),
|
|
106
|
-
|
|
135
|
+
segments.map(
|
|
136
|
+
(segment) => segment.kind === "agent" ? /* @__PURE__ */ jsx(PartList, { parts: segment.parts, components: threadPartRenderers }, segment.key) : /* @__PURE__ */ jsx(Fragment, { children: segment.render({
|
|
137
|
+
part: segment.part,
|
|
138
|
+
messageId: message.id,
|
|
139
|
+
partId: segment.part.id,
|
|
140
|
+
settled,
|
|
141
|
+
role: message.role
|
|
142
|
+
}) }, segment.key)
|
|
143
|
+
)
|
|
107
144
|
] })
|
|
108
145
|
}
|
|
109
146
|
);
|
|
110
147
|
}
|
|
148
|
+
function areMessageBlockPropsEqual(prev, next) {
|
|
149
|
+
return prev.message === next.message && prev.streaming === next.streaming && prev.renderers === next.renderers && prev.fallbackRenderer === next.fallbackRenderer;
|
|
150
|
+
}
|
|
151
|
+
const MessageBlock = memo(MessageBlockImpl, areMessageBlockPropsEqual);
|
|
152
|
+
const EMPTY_RENDERERS = {};
|
|
111
153
|
function ThreadTranscript({
|
|
112
154
|
messages,
|
|
113
155
|
liveParts,
|
|
114
|
-
liveStatus
|
|
156
|
+
liveStatus,
|
|
157
|
+
renderers: renderersProp,
|
|
158
|
+
fallbackRenderer = DEFAULT_FALLBACK_RENDERER
|
|
115
159
|
}) {
|
|
160
|
+
const renderers = useMemo(() => renderersProp ?? EMPTY_RENDERERS, [renderersProp]);
|
|
161
|
+
const liveMessage = useMemo(() => {
|
|
162
|
+
if (liveParts === void 0 || liveParts.length === 0) return null;
|
|
163
|
+
return { id: "__live__", role: "assistant", parts: liveParts, createdAt: 0 };
|
|
164
|
+
}, [liveParts]);
|
|
116
165
|
return /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
|
|
117
|
-
messages.map((message) => /* @__PURE__ */ jsx(
|
|
118
|
-
|
|
166
|
+
messages.map((message) => /* @__PURE__ */ jsx(
|
|
167
|
+
MessageBlock,
|
|
168
|
+
{
|
|
169
|
+
message,
|
|
170
|
+
renderers,
|
|
171
|
+
fallbackRenderer
|
|
172
|
+
},
|
|
173
|
+
message.id
|
|
174
|
+
)),
|
|
175
|
+
liveMessage !== null && /* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-atomic": "false", "aria-relevant": "additions", children: /* @__PURE__ */ jsx(
|
|
119
176
|
MessageBlock,
|
|
120
177
|
{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
178
|
+
message: liveMessage,
|
|
179
|
+
streaming: liveStatus === "streaming",
|
|
180
|
+
renderers,
|
|
181
|
+
fallbackRenderer
|
|
124
182
|
}
|
|
125
183
|
) })
|
|
126
184
|
] });
|
|
127
185
|
}
|
|
128
186
|
export {
|
|
129
187
|
ThreadTranscript,
|
|
188
|
+
messageBlockRenderCounter,
|
|
130
189
|
threadPartRenderers
|
|
131
190
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import type { ToolCallPart } from '../agent/index.js';
|
|
3
|
+
export type ToolChipProps = {
|
|
4
|
+
readonly part: ToolCallPart;
|
|
5
|
+
readonly defaultExpanded?: boolean;
|
|
6
|
+
/** Rendered only in 'approval-requested'. Omit both to render a read-only chip. */
|
|
7
|
+
readonly onApprove?: (approvalId: string) => void;
|
|
8
|
+
readonly onDeny?: (approvalId: string, reason?: string) => void;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A collapsed-by-default row for a single tool-call part. All seven wire states render something
|
|
12
|
+
* sane; the switch above still gates an eighth (compile-time-unhandled) state as a tsc error via a
|
|
13
|
+
* `const _: never = part` assignment, but unlike a truly-closed switch, its `default` branch never
|
|
14
|
+
* throws at RUNTIME — a value from persisted/wire data that reaches here with a `state` this build
|
|
15
|
+
* doesn't recognize renders an inert 'unknown' chip instead of blanking the transcript.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <ToolChip part={part} />
|
|
19
|
+
*/
|
|
20
|
+
export declare function ToolChip({ part, defaultExpanded, onApprove, onDeny, }: ToolChipProps): JSX.Element;
|
|
21
|
+
//# sourceMappingURL=tool-chip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-chip.d.ts","sourceRoot":"","sources":["../../src/agent-chat/tool-chip.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AA4E5C,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAChE,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,eAAuB,EACvB,SAAS,EACT,MAAM,GACP,EAAE,aAAa,GAAG,GAAG,CAAC,OAAO,CAiG7B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Button, Collapse, Group, Stack, Text, UnstyledButton } from "@mantine/core";
|
|
3
|
+
import { useDisclosure } from "@mantine/hooks";
|
|
4
|
+
import { isToolCallSettled } from "../agent/index.js";
|
|
5
|
+
import { alpha, VX } from "../tokens/index.js";
|
|
6
|
+
const MICRO_LABEL_STYLE = {
|
|
7
|
+
fontFamily: "var(--basalt-font-mono)",
|
|
8
|
+
fontSize: VX.text.micro,
|
|
9
|
+
fontWeight: 500,
|
|
10
|
+
letterSpacing: "0.06em",
|
|
11
|
+
textTransform: "uppercase",
|
|
12
|
+
color: VX.faint
|
|
13
|
+
};
|
|
14
|
+
const RAIL_STYLE = {
|
|
15
|
+
borderLeft: `2px solid ${VX.divider}`,
|
|
16
|
+
paddingLeft: "var(--vx-space-agent-rail-inset-x)"
|
|
17
|
+
};
|
|
18
|
+
const CODE_BLOCK_STYLE = {
|
|
19
|
+
margin: 0,
|
|
20
|
+
marginTop: "var(--vx-space-agent-part-gap-top)",
|
|
21
|
+
fontFamily: "var(--basalt-font-mono)",
|
|
22
|
+
backgroundColor: alpha(VX.ink, 0.06),
|
|
23
|
+
borderRadius: "var(--vx-radius-card)",
|
|
24
|
+
padding: "var(--vx-space-agent-code-inset)",
|
|
25
|
+
whiteSpace: "pre-wrap",
|
|
26
|
+
overflowWrap: "anywhere"
|
|
27
|
+
};
|
|
28
|
+
function stateMeta(part) {
|
|
29
|
+
switch (part.state) {
|
|
30
|
+
case "input-streaming":
|
|
31
|
+
return { label: "streaming", dotToken: VX.status.neutral };
|
|
32
|
+
case "input-available":
|
|
33
|
+
return { label: "running", dotToken: VX.status.neutral };
|
|
34
|
+
case "approval-requested":
|
|
35
|
+
return { label: "needs approval", dotToken: VX.status.warn };
|
|
36
|
+
case "approval-responded":
|
|
37
|
+
return part.approval.approved ? { label: "approved", dotToken: VX.status.neutral } : { label: "declined", dotToken: VX.status.bad };
|
|
38
|
+
case "output-available":
|
|
39
|
+
return part.preliminary === true ? { label: "preliminary", dotToken: VX.status.warn } : { label: "done", dotToken: VX.status.good };
|
|
40
|
+
case "output-error":
|
|
41
|
+
return { label: "error", dotToken: VX.status.bad };
|
|
42
|
+
case "output-denied":
|
|
43
|
+
return { label: "denied", dotToken: VX.status.bad };
|
|
44
|
+
default: {
|
|
45
|
+
const exhaustive = part;
|
|
46
|
+
void exhaustive;
|
|
47
|
+
return { label: "unknown", dotToken: VX.status.neutral };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function formatDuration(ms) {
|
|
52
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms)}ms`;
|
|
53
|
+
}
|
|
54
|
+
function ToolChip({
|
|
55
|
+
part,
|
|
56
|
+
defaultExpanded = false,
|
|
57
|
+
onApprove,
|
|
58
|
+
onDeny
|
|
59
|
+
}) {
|
|
60
|
+
const [expanded, { toggle }] = useDisclosure(defaultExpanded);
|
|
61
|
+
const { label, dotToken } = stateMeta(part);
|
|
62
|
+
const settled = isToolCallSettled(part);
|
|
63
|
+
const input = "input" in part ? part.input : void 0;
|
|
64
|
+
const rawInput = part.state === "output-error" ? part.rawInput : void 0;
|
|
65
|
+
return /* @__PURE__ */ jsx(Box, { style: RAIL_STYLE, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
66
|
+
/* @__PURE__ */ jsx(UnstyledButton, { onClick: toggle, children: /* @__PURE__ */ jsxs(Group, { gap: 6, align: "center", wrap: "nowrap", children: [
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
Box,
|
|
69
|
+
{
|
|
70
|
+
"aria-hidden": true,
|
|
71
|
+
style: {
|
|
72
|
+
width: 6,
|
|
73
|
+
height: 6,
|
|
74
|
+
flexShrink: 0,
|
|
75
|
+
borderRadius: "50%",
|
|
76
|
+
// theme-allow: a 6px state dot is a circle, not a surface corner — no radius token expresses it
|
|
77
|
+
backgroundColor: dotToken
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
/* @__PURE__ */ jsx(Text, { style: MICRO_LABEL_STYLE, children: part.toolName }),
|
|
82
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: label }),
|
|
83
|
+
part.durationMs !== void 0 && /* @__PURE__ */ jsx(
|
|
84
|
+
Text,
|
|
85
|
+
{
|
|
86
|
+
style: {
|
|
87
|
+
fontFamily: "var(--basalt-font-mono)",
|
|
88
|
+
fontSize: VX.text.micro,
|
|
89
|
+
color: VX.faint
|
|
90
|
+
},
|
|
91
|
+
children: formatDuration(part.durationMs)
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
!settled && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\u2026" })
|
|
95
|
+
] }) }),
|
|
96
|
+
/* @__PURE__ */ jsx(Collapse, { expanded, children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
|
|
97
|
+
input !== void 0 && /* @__PURE__ */ jsx(Text, { component: "pre", size: "xs", style: CODE_BLOCK_STYLE, children: JSON.stringify(input, null, 2) }),
|
|
98
|
+
rawInput !== void 0 && /* @__PURE__ */ jsx(Text, { component: "pre", size: "xs", style: CODE_BLOCK_STYLE, children: JSON.stringify(rawInput, null, 2) }),
|
|
99
|
+
part.state === "output-available" && /* @__PURE__ */ jsx(Text, { component: "pre", size: "xs", style: CODE_BLOCK_STYLE, children: JSON.stringify(part.output, null, 2) }),
|
|
100
|
+
part.state === "output-error" && /* @__PURE__ */ jsx(Text, { size: "xs", style: { color: VX.status.bad }, children: part.errorText }),
|
|
101
|
+
part.state === "output-denied" && part.approval.reason !== void 0 && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: part.approval.reason }),
|
|
102
|
+
part.state === "approval-requested" && (onApprove !== void 0 || onDeny !== void 0) && /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
|
|
103
|
+
onApprove !== void 0 && /* @__PURE__ */ jsx(Button, { size: "compact-sm", onClick: () => onApprove(part.approval.id), children: "Approve" }),
|
|
104
|
+
onDeny !== void 0 && /* @__PURE__ */ jsx(
|
|
105
|
+
Button,
|
|
106
|
+
{
|
|
107
|
+
size: "compact-sm",
|
|
108
|
+
variant: "light",
|
|
109
|
+
color: "red",
|
|
110
|
+
onClick: () => onDeny(part.approval.id),
|
|
111
|
+
children: "Deny"
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] })
|
|
115
|
+
] }) })
|
|
116
|
+
] }) });
|
|
117
|
+
}
|
|
118
|
+
export {
|
|
119
|
+
ToolChip
|
|
120
|
+
};
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -126,6 +126,25 @@ export type BasaltConfig = {
|
|
|
126
126
|
* { exemptRules: { 'inline-display': ['agent'] } } // inline-display never fires under src/agent/**
|
|
127
127
|
*/
|
|
128
128
|
exemptRules?: Partial<Record<GuardKind, string[]>>;
|
|
129
|
+
/**
|
|
130
|
+
* Declares an intentional `ai` package major-version skew across workspace packages, exempting
|
|
131
|
+
* `doctor`'s `ai-major-parity` hard check. The value IS the reason — a bare `true` is rejected,
|
|
132
|
+
* because the whole point of this key is that the pairing is written down, not just switched off.
|
|
133
|
+
* A written declaration is the pin `basalt/ai-sdk-major`'s own comment notes is otherwise missing.
|
|
134
|
+
*
|
|
135
|
+
* Semantics: absent (or present but not a non-empty string) → the skew still hard-fails exactly as
|
|
136
|
+
* without this key. A non-empty string with a skew present → `doctor` passes, echoing both the
|
|
137
|
+
* skew and this reason. A non-empty string with NO skew present → `doctor` warns that the
|
|
138
|
+
* exemption is stale and can be deleted (an exemption nobody re-checks is how a real, later skew
|
|
139
|
+
* slips through unnoticed).
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* // apps/api streams on ai@5; apps/dashboard parses on ai@7; a producer-side TransformStream
|
|
143
|
+
* // neutralizes the one enum value that differs between the two majors.
|
|
144
|
+
* { aiMajorSkewReason: 'apps/api pinned to ai@5, apps/dashboard on ai@7 — the skew is neutralized '
|
|
145
|
+
* + 'by a producer-side TransformStream in apps/api/src/stream-transform.ts' }
|
|
146
|
+
*/
|
|
147
|
+
aiMajorSkewReason?: string;
|
|
129
148
|
};
|
|
130
149
|
/**
|
|
131
150
|
* Theme guard — thin FS walker over the headless `../guard` core. Reads BasaltConfig, builds a
|
|
@@ -324,6 +343,17 @@ type DoctorResult = {
|
|
|
324
343
|
*
|
|
325
344
|
* Hard failures (exit non-zero):
|
|
326
345
|
* 1. .basalt/manifest.json exists (init was run).
|
|
346
|
+
* 6. `ai-major-parity`: every workspace package that declares the `ai` package agrees on its
|
|
347
|
+
* major version. `basalt/ai-sdk-major` (the oxlint plugin rule) cannot catch this — it checks
|
|
348
|
+
* a linted file's NEAREST package.json, so a lint run scoped to one workspace package only
|
|
349
|
+
* ever sees that package's own `ai` major and is perfectly happy; the cross-package skew (one
|
|
350
|
+
* package streaming on `ai@5`, another parsing it on `ai@7`) only a repo-wide manifest walk
|
|
351
|
+
* can see. Skipped entirely when `cwd` has no `workspaces` field or none of its packages
|
|
352
|
+
* declare `ai`. A detected skew is exempt-able via `basalt.aiMajorSkewReason` (a non-empty
|
|
353
|
+
* reason string, not a bare `true`) in the consumer's package.json — an intentional
|
|
354
|
+
* producer/consumer split neutralized by a transform is a locked decision, not a defect, and
|
|
355
|
+
* the written reason IS the pin that a permanently-failing guard would otherwise lack. Missing
|
|
356
|
+
* or invalid (empty/non-string) still hard-fails exactly as if the key were absent.
|
|
327
357
|
*
|
|
328
358
|
* Warnings (non-fatal):
|
|
329
359
|
* 2. The installed node_modules/basalt-ui version matches the manifest's basaltVersion
|
|
@@ -335,6 +365,9 @@ type DoctorResult = {
|
|
|
335
365
|
* `bunx basalt-ui` npm fetch; best-effort, skipped if node_modules is absent).
|
|
336
366
|
* 5. `basaltAppPlugin`'s (basalt-ui/vite) default icon filenames exist under public/ (only when
|
|
337
367
|
* a public/ dir exists at all — skipped otherwise).
|
|
368
|
+
* 7. A declared `basalt.aiMajorSkewReason` when the ai majors currently AGREE — the exemption is
|
|
369
|
+
* stale and can be deleted; an exemption nobody revisits is how a real, later skew slips
|
|
370
|
+
* through unnoticed.
|
|
338
371
|
*
|
|
339
372
|
* Returns the exit code: 0 = all good, 1 = one or more hard failures.
|
|
340
373
|
*/
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAwB,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE9E,OAAO,EAAE,UAAU,EAAE,WAAW,EAA2C,MAAM,aAAa,CAAA;AAI9F,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG;IACzB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;wFACoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;IACpD,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -783,7 +783,105 @@ function checkCoverage() {
|
|
|
783
783
|
}
|
|
784
784
|
return 1;
|
|
785
785
|
}
|
|
786
|
+
function subdirNames(base, options = {}) {
|
|
787
|
+
let entries;
|
|
788
|
+
try {
|
|
789
|
+
entries = readdirSync(base);
|
|
790
|
+
} catch {
|
|
791
|
+
return [];
|
|
792
|
+
}
|
|
793
|
+
return entries.filter((name) => {
|
|
794
|
+
if (name === "node_modules") return false;
|
|
795
|
+
if (options.skipDotDirs && name.startsWith(".")) return false;
|
|
796
|
+
try {
|
|
797
|
+
return statSync(resolve(base, name)).isDirectory();
|
|
798
|
+
} catch {
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
function expandPatternSegments(baseDir, segments) {
|
|
804
|
+
if (segments.length === 0) return [baseDir];
|
|
805
|
+
const [head, ...rest] = segments;
|
|
806
|
+
if (head === "**") {
|
|
807
|
+
const matches = expandPatternSegments(baseDir, rest);
|
|
808
|
+
for (const name of subdirNames(baseDir, { skipDotDirs: true })) {
|
|
809
|
+
matches.push(...expandPatternSegments(resolve(baseDir, name), segments));
|
|
810
|
+
}
|
|
811
|
+
return matches;
|
|
812
|
+
}
|
|
813
|
+
const names = head === "*" ? subdirNames(baseDir, { skipDotDirs: true }) : subdirNames(baseDir).filter((n) => n === head);
|
|
814
|
+
return names.flatMap((name) => expandPatternSegments(resolve(baseDir, name), rest));
|
|
815
|
+
}
|
|
816
|
+
function expandWorkspaceGlobs(cwd, patterns) {
|
|
817
|
+
const expand = (pattern) => expandPatternSegments(
|
|
818
|
+
cwd,
|
|
819
|
+
pattern.split("/").filter((s) => s.length > 0)
|
|
820
|
+
);
|
|
821
|
+
const excluded = new Set(
|
|
822
|
+
patterns.filter((pattern) => pattern.startsWith("!")).flatMap((pattern) => expand(pattern.replace(/^!+/, "")))
|
|
823
|
+
);
|
|
824
|
+
const dirs = [];
|
|
825
|
+
for (const pattern of patterns) {
|
|
826
|
+
if (pattern.startsWith("!")) continue;
|
|
827
|
+
for (const abs of expand(pattern)) {
|
|
828
|
+
if (excluded.has(abs)) continue;
|
|
829
|
+
if (existsSync(resolve(abs, "package.json"))) dirs.push(abs);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return dirs;
|
|
833
|
+
}
|
|
834
|
+
function collectWorkspacePackages(cwd) {
|
|
835
|
+
const raw = readIfExists(resolve(cwd, "package.json"));
|
|
836
|
+
if (raw === null) return { packages: [], hasWorkspacesField: false };
|
|
837
|
+
let parsed;
|
|
838
|
+
try {
|
|
839
|
+
parsed = JSON.parse(raw);
|
|
840
|
+
} catch {
|
|
841
|
+
return { packages: [], hasWorkspacesField: false };
|
|
842
|
+
}
|
|
843
|
+
const patterns = isStringArray(parsed.workspaces) ? parsed.workspaces : parsed.workspaces !== null && typeof parsed.workspaces === "object" && isStringArray(parsed.workspaces.packages) ? parsed.workspaces.packages : null;
|
|
844
|
+
if (patterns === null) return { packages: [], hasWorkspacesField: false };
|
|
845
|
+
const rootName = parsed.name && parsed.name.length > 0 ? `${parsed.name} (root)` : "(root)";
|
|
846
|
+
const workspacePackages = expandWorkspaceGlobs(cwd, patterns).map((dir) => ({
|
|
847
|
+
name: readPackageName(dir),
|
|
848
|
+
dir
|
|
849
|
+
}));
|
|
850
|
+
return {
|
|
851
|
+
packages: [{ name: rootName, dir: cwd }, ...workspacePackages],
|
|
852
|
+
hasWorkspacesField: true
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
function isStringArray(value) {
|
|
856
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
857
|
+
}
|
|
858
|
+
function majorOf(range) {
|
|
859
|
+
if (typeof range !== "string") return null;
|
|
860
|
+
const match = range.match(/\d+/);
|
|
861
|
+
return match === null ? null : Number(match[0]);
|
|
862
|
+
}
|
|
863
|
+
function aiMajorAt(dir) {
|
|
864
|
+
const raw = readIfExists(resolve(dir, "package.json"));
|
|
865
|
+
if (raw === null) return null;
|
|
866
|
+
try {
|
|
867
|
+
const pkg = JSON.parse(raw);
|
|
868
|
+
return majorOf(
|
|
869
|
+
pkg.dependencies?.["ai"] ?? pkg.devDependencies?.["ai"] ?? pkg.peerDependencies?.["ai"]
|
|
870
|
+
);
|
|
871
|
+
} catch {
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function resolveAiMajorSkewReason(cfg) {
|
|
876
|
+
const raw = cfg.aiMajorSkewReason;
|
|
877
|
+
if (raw === void 0) return { reason: null, presentButInvalid: false };
|
|
878
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
879
|
+
return { reason: raw, presentButInvalid: false };
|
|
880
|
+
}
|
|
881
|
+
return { reason: null, presentButInvalid: true };
|
|
882
|
+
}
|
|
786
883
|
function doctor(cwd = process.cwd()) {
|
|
884
|
+
const cfg = readBasaltConfig(cwd);
|
|
787
885
|
const result = { hardFailures: 0, warnings: 0 };
|
|
788
886
|
const lines = [`
|
|
789
887
|
basalt-ui doctor \u2014 ${cwd}
|
|
@@ -881,6 +979,39 @@ basalt-ui doctor \u2014 ${cwd}
|
|
|
881
979
|
pass("public/ has all of basaltAppPlugin's default icon files");
|
|
882
980
|
}
|
|
883
981
|
}
|
|
982
|
+
const workspaceWalk = collectWorkspacePackages(cwd);
|
|
983
|
+
const aiMajors = workspaceWalk.packages.map((pkg) => ({ name: pkg.name, major: aiMajorAt(pkg.dir) })).filter((entry) => entry.major !== null);
|
|
984
|
+
const { reason: aiMajorSkewReason, presentButInvalid: aiMajorSkewReasonInvalid } = resolveAiMajorSkewReason(cfg);
|
|
985
|
+
if (aiMajors.length > 0) {
|
|
986
|
+
const distinctMajors = new Set(aiMajors.map((entry) => entry.major));
|
|
987
|
+
if (distinctMajors.size > 1) {
|
|
988
|
+
const summary = aiMajors.map((entry) => `${entry.name}@ai${entry.major}`).join(", ");
|
|
989
|
+
if (aiMajorSkewReason !== null) {
|
|
990
|
+
pass(
|
|
991
|
+
`ai package major version mismatch across workspace packages: ${summary} \u2014 exempted via basalt.aiMajorSkewReason: "${aiMajorSkewReason}"`
|
|
992
|
+
);
|
|
993
|
+
} else if (aiMajorSkewReasonInvalid) {
|
|
994
|
+
fail(
|
|
995
|
+
`ai package major version mismatch across workspace packages: ${summary} \u2014 basalt.aiMajorSkewReason is present but is not a non-empty string (a bare \`true\` is not accepted); the exemption must carry a written reason, e.g. \`aiMajorSkewReason: "apps/api on ai@5, apps/dashboard on ai@7, neutralized by a producer-side TransformStream"\`.`
|
|
996
|
+
);
|
|
997
|
+
} else {
|
|
998
|
+
fail(`ai package major version mismatch across workspace packages: ${summary}`);
|
|
999
|
+
}
|
|
1000
|
+
} else {
|
|
1001
|
+
const [major] = distinctMajors;
|
|
1002
|
+
if (aiMajorSkewReason !== null) {
|
|
1003
|
+
warn(
|
|
1004
|
+
`basalt.aiMajorSkewReason ("${aiMajorSkewReason}") is declared but the ai package major already matches across all ${aiMajors.length} workspace package(s) declaring it (ai@${major}) \u2014 the exemption is no longer needed and can be deleted.`
|
|
1005
|
+
);
|
|
1006
|
+
} else {
|
|
1007
|
+
pass(
|
|
1008
|
+
`ai package major matches across ${aiMajors.length} workspace package(s) declaring it (ai@${major})`
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
} else if (workspaceWalk.hasWorkspacesField) {
|
|
1013
|
+
pass("ai-major-parity: no workspace package declares the ai dependency \u2014 nothing to compare");
|
|
1014
|
+
}
|
|
884
1015
|
lines.push("");
|
|
885
1016
|
if (result.hardFailures === 0 && result.warnings === 0) {
|
|
886
1017
|
lines.push("All checks passed.");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE7E,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AAoP9D,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,EAAE,WAoBlC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE7E,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AAoP9D,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,EAAE,WAoBlC,CAAA;AAwTD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;gCA0BL,WAAW;;;;;;;gCAcX,WAAW;;;;;gCAMX,WAAW;;;;;;;gCAaX,WAAW;;;;;;;gCAQX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCASX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCAaX,WAAW;;;;;;gCAMX,WAAW;;;;;;;gCAaX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;gCASX,WAAW;;;;;;;gCASX,WAAW;;;;;;;gCAUX,WAAW;;;;CAOmB,CAAA;AAUjD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,EAAE,CA2PtF"}
|