Skip to content

Commit e4f18fb

Browse files
cristipufuclaude
andcommitted
feat: add functions support, agent improvements, explorer live reload
- Add coded functions docs (SKILL.md, creating-functions.md, etc.) - Document uipath.json, bindings.json, pack/publish workflow - Add evaluator file creation step in evaluations docs - Fix explorer sidebar not refreshing on file add/delete - Fix UTF-8 encoding for agent bash tool on Windows - Improve agent prompt: full build cycle, force bash usage - Remove em dash from reload toast - Bump version to 0.0.62 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f8ed42e commit e4f18fb

File tree

18 files changed

+584
-110
lines changed

18 files changed

+584
-110
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-dev"
3-
version = "0.0.61"
3+
version = "0.0.62"
44
description = "UiPath Developer Console"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

src/uipath/dev/server/frontend/src/components/agent/AgentChatSidebar.tsx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,27 @@ export default function AgentChatSidebar() {
8686

8787
const isBusy = status === "thinking" || status === "executing" || status === "planning";
8888

89+
const textareaRef = useRef<HTMLTextAreaElement>(null);
90+
91+
const resetTextareaHeight = () => {
92+
const ta = textareaRef.current;
93+
if (!ta) return;
94+
ta.style.height = "auto";
95+
ta.style.height = Math.min(ta.scrollHeight, 200) + "px";
96+
};
97+
8998
const handleSend = useCallback(() => {
9099
const text = input.trim();
91100
if (!text || !selectedModel || isBusy) return;
92101
stickToBottom.current = true;
93102
addUserMessage(text);
94103
ws.sendAgentMessage(text, selectedModel, sessionId, selectedSkillIds);
95104
setInput("");
105+
// Reset textarea height after send
106+
requestAnimationFrame(() => {
107+
const ta = textareaRef.current;
108+
if (ta) ta.style.height = "auto";
109+
});
96110
}, [input, selectedModel, isBusy, sessionId, selectedSkillIds, addUserMessage, ws]);
97111

98112
const handleStop = useCallback(() => {
@@ -164,9 +178,15 @@ export default function AgentChatSidebar() {
164178
className="h-full overflow-y-auto px-3 py-2 space-y-0.5"
165179
>
166180
{messages.length === 0 && (
167-
<p className="text-[var(--text-muted)] text-sm text-center py-6">
168-
No messages yet
169-
</p>
181+
<div className="flex flex-col items-center justify-center py-10 px-4 gap-3" style={{ color: "var(--text-muted)" }}>
182+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
183+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
184+
</svg>
185+
<div className="text-center space-y-1.5">
186+
<p className="text-sm font-medium" style={{ color: "var(--text-secondary)" }}>Ask the agent to help you code</p>
187+
<p className="text-xs leading-relaxed">Create agents, functions, evaluations,<br />or ask questions about your project.</p>
188+
</div>
189+
</div>
170190
)}
171191
{messages.map((msg) => (
172192
<AgentMessageComponent key={msg.id} message={msg} />
@@ -196,24 +216,29 @@ export default function AgentChatSidebar() {
196216
)}
197217
</div>
198218

199-
{/* Input — matches ChatInput from debug view */}
219+
{/* Input */}
200220
<div
201-
className="flex items-center gap-2 px-3 py-2 border-t"
221+
className="flex items-end gap-2 px-3 py-2 border-t"
202222
style={{ borderColor: "var(--border)" }}
203223
>
204-
<input
224+
<textarea
225+
ref={textareaRef}
205226
value={input}
206-
onChange={(e) => setInput(e.target.value)}
227+
onChange={(e) => {
228+
setInput(e.target.value);
229+
resetTextareaHeight();
230+
}}
207231
onKeyDown={handleKeyDown}
208232
disabled={isBusy || !selectedModel}
209233
placeholder={isBusy ? "Waiting for response..." : "Message..."}
210-
className="flex-1 bg-transparent text-sm py-1 disabled:opacity-40 placeholder:text-[var(--text-muted)]"
211-
style={{ color: "var(--text-primary)" }}
234+
rows={2}
235+
className="flex-1 bg-transparent text-sm py-1 disabled:opacity-40 placeholder:text-[var(--text-muted)] resize-none"
236+
style={{ color: "var(--text-primary)", maxHeight: 200, overflow: "auto" }}
212237
/>
213238
<button
214239
onClick={handleSend}
215240
disabled={!canSend}
216-
className="text-xs font-semibold px-3 py-1.5 rounded transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"
241+
className="text-xs font-semibold px-3 py-1.5 rounded transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed shrink-0"
217242
aria-label="Send message"
218243
style={{
219244
color: canSend ? "var(--accent)" : "var(--text-muted)",

src/uipath/dev/server/frontend/src/components/agent/AgentMessage.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,17 @@ function SingleToolCall({ tc }: { tc: AgentToolCall }) {
189189
);
190190
}
191191

192+
const VISIBLE_TOOL_CALLS = 3;
193+
192194
function ToolCard({ message }: Props) {
193195
const calls = message.toolCalls ?? (message.toolCall ? [message.toolCall] : []);
196+
const [showAll, setShowAll] = useState(false);
194197
if (calls.length === 0) return null;
195198

199+
const hiddenCount = calls.length - VISIBLE_TOOL_CALLS;
200+
const shouldCollapse = hiddenCount > 0 && !showAll;
201+
const visibleCalls = shouldCollapse ? calls.slice(-VISIBLE_TOOL_CALLS) : calls;
202+
196203
return (
197204
<div className="py-1.5">
198205
<div className="flex items-center gap-1.5 mb-0.5">
@@ -202,8 +209,24 @@ function ToolCard({ message }: Props) {
202209
</span>
203210
</div>
204211
<div className="space-y-1">
205-
{calls.map((tc, i) => (
206-
<SingleToolCall key={i} tc={tc} />
212+
{shouldCollapse && (
213+
<button
214+
onClick={() => setShowAll(true)}
215+
className="ml-2.5 inline-flex items-center gap-1 text-[11px] font-mono px-2 py-1 rounded cursor-pointer hover:brightness-125"
216+
style={{
217+
background: "var(--bg-primary)",
218+
border: "1px solid var(--border)",
219+
color: "var(--text-muted)",
220+
}}
221+
>
222+
{hiddenCount} more tool {hiddenCount === 1 ? "call" : "calls"}
223+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{ marginLeft: 2 }}>
224+
<path d="M6 9l6 6 6-6" />
225+
</svg>
226+
</button>
227+
)}
228+
{visibleCalls.map((tc, i) => (
229+
<SingleToolCall key={shouldCollapse ? i + hiddenCount : i} tc={tc} />
207230
))}
208231
</div>
209232
</div>

src/uipath/dev/server/frontend/src/components/shared/ReloadToast.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default function ReloadToast() {
2626
<div className="fixed top-4 left-1/2 -translate-x-1/2 z-50 flex items-center justify-between px-5 py-2.5 rounded-lg shadow-lg min-w-[400px]"
2727
style={{ background: "var(--bg-secondary)", border: "1px solid var(--bg-tertiary)" }}>
2828
<span className="text-sm" style={{ color: "var(--text-secondary)" }}>
29-
Files changed reload to apply
29+
Files changed, reload to apply
3030
</span>
3131
<div className="flex items-center gap-2">
3232
<button

src/uipath/dev/server/frontend/src/store/useWebSocket.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useRunStore } from "./useRunStore";
44
import { useEvalStore } from "./useEvalStore";
55
import { useAgentStore } from "./useAgentStore";
66
import { useExplorerStore } from "./useExplorerStore";
7-
import { readFile } from "../api/explorer-client";
7+
import { readFile, listDirectory } from "../api/explorer-client";
88
import type { RunSummary, TraceSpan, LogEntry, InterruptEvent } from "../types/run";
99
import type { EvalRunSummary, EvalItemResult } from "../types/eval";
1010
import type { AgentPlanItem, AgentStatus } from "../types/agent";
@@ -72,6 +72,7 @@ export function useWebSocket() {
7272
const changedFiles = msg.payload.files as string[];
7373
const changedSet = new Set(changedFiles);
7474
const explorer = useExplorerStore.getState();
75+
// Refresh open tab contents
7576
for (const tab of explorer.openTabs) {
7677
if (explorer.dirty[tab] || !changedSet.has(tab)) continue;
7778
readFile(tab).then((fc) => {
@@ -81,6 +82,20 @@ export function useWebSocket() {
8182
s.setFileContent(tab, fc);
8283
}).catch(() => {});
8384
}
85+
// Refresh directory listings for already-loaded parent dirs
86+
const dirsToRefresh = new Set<string>();
87+
for (const filePath of changedFiles) {
88+
const lastSlash = filePath.lastIndexOf("/");
89+
const parentDir = lastSlash === -1 ? "" : filePath.substring(0, lastSlash);
90+
if (parentDir in explorer.children) {
91+
dirsToRefresh.add(parentDir);
92+
}
93+
}
94+
for (const dir of dirsToRefresh) {
95+
listDirectory(dir)
96+
.then((entries) => useExplorerStore.getState().setChildren(dir, entries))
97+
.catch(() => {});
98+
}
8499
break;
85100
}
86101
// Eval events

src/uipath/dev/server/routes/files.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,5 +191,8 @@ async def save_file(path: str, body: SaveFileBody) -> dict[str, str]:
191191
"""Save file content. Creates parent dirs if needed."""
192192
target = _resolve_safe(path)
193193
target.parent.mkdir(parents=True, exist_ok=True)
194-
target.write_text(body.content, encoding="utf-8")
194+
# Write bytes directly to avoid Python text-mode converting \n → \r\n on
195+
# Windows, which would add extra carriage returns on every save since
196+
# Monaco normalizes line endings to \n.
197+
target.write_bytes(body.content.encode("utf-8"))
195198
return {"status": "ok"}

src/uipath/dev/server/static/assets/ChatPanel-CiK6YSHu.js renamed to src/uipath/dev/server/static/assets/ChatPanel-BcW6Jtpz.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uipath/dev/server/static/assets/index-Gpw0SLbu.js renamed to src/uipath/dev/server/static/assets/index-B2xfJE6O.js

Lines changed: 32 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uipath/dev/server/static/assets/index-BhpA3bEW.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/uipath/dev/server/static/assets/index-DKf_uUe0.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)