Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .codex/review-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ Every comment must be traceable to changed behavior in this PR and anchored to a

#### Security


- Injection risks (SQL, command, XSS) when handling user input.
- Hardcoded secrets — API keys, passwords, tokens in code.
- Missing input validation at system boundaries (user input, external APIs). Not for internal function calls.
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/check-package-lock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ on:
branches:
- main
paths:
- 'package.json'
- 'package-lock.json'
- '**/package.json'
- "package.json"
- "package-lock.json"
- "**/package.json"
pull_request:
branches:
- "**"
paths:
- 'package.json'
- 'package-lock.json'
- '**/package.json'
- "package.json"
- "package-lock.json"
- "**/package.json"

jobs:
verify-package-lock:
Expand Down Expand Up @@ -55,7 +55,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
node-version: "22"

- name: Validate package-lock.json is valid and in sync
run: npm ci --dry-run --ignore-scripts
2 changes: 1 addition & 1 deletion .github/workflows/validate-plugin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ jobs:
echo "✅ Core Functionality tests found"
echo "✅ Error Handling tests found"
echo "✅ All tests passing"
fi
fi
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ Before you consider a task "done", you must:
# Task: [<task number (last + 1)>] <feature>

## Goal

- <short goal>

## Subtasks

- [ ] <subtask 1>
- [ ] <subtask 2>

## Notes

- <risks, decisions, links>
```

Expand Down
49 changes: 0 additions & 49 deletions apps/agent/fix-uuid.js

This file was deleted.

17 changes: 8 additions & 9 deletions apps/agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
"test:ragas": "NODE_OPTIONS='--import tsx' tsx tests/ragas/evaluate.ts",
"test:ragas:results": "NODE_OPTIONS='--import tsx' tsx tests/ragas/show-results.ts",
"test:ragas:dashboard": "NODE_OPTIONS='--import tsx' tsx tests/ragas/dashboard.ts",
"ragas": "chmod +x tests/ragas/run-ragas.sh && tests/ragas/run-ragas.sh",
"postinstall": "node fix-uuid.js"
"ragas": "chmod +x tests/ragas/run-ragas.sh && tests/ragas/run-ragas.sh"
},
"dependencies": {
"@dkg/expo-forcegraph": "^0.0.0",
Expand All @@ -39,13 +38,13 @@
"@expo-google-fonts/manrope": "^0.4.1",
"@expo-google-fonts/space-grotesk": "^0.4.0",
"@expo/vector-icons": "^14.1.0",
"@langchain/anthropic": "^0.3.28",
"@langchain/core": "^0.3.66",
"@langchain/google-genai": "^0.2.18",
"@langchain/groq": "^0.2.4",
"@langchain/mistralai": "^0.2.1",
"@langchain/openai": "^0.6.3",
"@langchain/xai": "^0.1.0",
"@langchain/anthropic": "^1.3.22",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Issue: This upgrades LangChain packages across major versions, but the PR does not add regression coverage for the changed content-shape handling in chat parsing. Add tests for both string/object image_url blocks and non-string/missing text fields so migration regressions are caught.

"@langchain/core": "^1.1.30",
"@langchain/google-genai": "^2.1.24",
"@langchain/groq": "^1.1.4",
"@langchain/mistralai": "^1.0.7",
"@langchain/openai": "^1.2.12",
"@langchain/xai": "^1.3.8",
"@modelcontextprotocol/sdk": "^1.16.0",
"@node-rs/argon2": "^2.0.2",
"@react-native-async-storage/async-storage": "2.1.2",
Expand Down
34 changes: 22 additions & 12 deletions apps/agent/src/app/(protected)/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ export default function ChatPage() {

const s = toToolExecutionSettings(mode);
await settings.set("autoApproveMcpTools", s.autoApproveMcpTools);
await settings.set("showMcpToolExecutionPanels", s.showMcpToolExecutionPanels);
await settings.set(
"showMcpToolExecutionPanels",
s.showMcpToolExecutionPanels,
);
await settings.reload();
},
[settings, tools],
Expand Down Expand Up @@ -180,8 +183,7 @@ export default function ChatPage() {
continue;
}

const existingId =
typeof tc.id === "string" ? tc.id.trim() : "";
const existingId = typeof tc.id === "string" ? tc.id.trim() : "";
normalizedToolCalls.push({
...tc,
id: existingId || `local-tool-call-${localToolCallIdCounter.current++}`,
Expand Down Expand Up @@ -507,8 +509,8 @@ export default function ChatPage() {
parsedContent.metadata
.at(0)
?.[
"https://ontology.origintrail.io/dkg/1.0#publishTime"
]?.at(0)?.["@value"] ?? Date.now(),
"https://ontology.origintrail.io/dkg/1.0#publishTime"
]?.at(0)?.["@value"] ?? Date.now(),
).getTime(),
txHash: parsedContent.metadata
.at(0)
Expand Down Expand Up @@ -540,7 +542,7 @@ export default function ChatPage() {
parsedContent.metadata
.at(0)
?.["https://ontology.origintrail.io/dkg/1.0#publishTx"]?.at(0)?.[
"@value"
"@value"
] ?? "unknown";
resolved.publisher =
parsedContent.metadata
Expand Down Expand Up @@ -638,7 +640,7 @@ export default function ChatPage() {

for (const c of toContents(m.content)) {
if (c.type === "image_url") {
images.push({ uri: c.image_url });
images.push({ uri: c.image_url as string });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: c.image_url as string can pass an object payload through as uri at runtime (e.g. { url: ... }), which breaks image rendering. Normalize explicitly (typeof c.image_url === 'string' ? c.image_url : c.image_url?.url) and only push when a string URL exists; same unsafe-cast pattern appears at line 663 for c.text.

continue;
}

Expand All @@ -658,7 +660,7 @@ export default function ChatPage() {
continue;
}

text.push(c.text);
text.push(c.text as string);
}
}

Expand All @@ -667,7 +669,8 @@ export default function ChatPage() {
const allToolCallsHidden =
hasToolCalls &&
m.tool_calls!.every((tc) => {
const isAutoApproved = autoApproveTools || tools.isAllowedForSession(tc.name);
const isAutoApproved =
autoApproveTools || tools.isAllowedForSession(tc.name);
return isAutoApproved && !showToolExecutionPanels;
});

Expand Down Expand Up @@ -792,7 +795,10 @@ export default function ChatPage() {
lastUserMessageYRef.current = y;
if (scrollPendingRef.current) {
scrollPendingRef.current = false;
scrollTargetRef.current = Math.max(0, y - SCROLL_TOP_GAP);
scrollTargetRef.current = Math.max(
0,
y - SCROLL_TOP_GAP,
);
setContentMinHeight(y + messagesViewHeight);
}
}}
Expand All @@ -804,11 +810,15 @@ export default function ChatPage() {

return <View key={i}>{messageContent}</View>;
})}
{isThinkingVisible(isGenerating, streamingContent) && <Chat.Thinking />}
{isThinkingVisible(isGenerating, streamingContent) && (
<Chat.Thinking />
)}
{streamingContent !== null && (
<Chat.Message icon="assistant">
<Markdown>
{normalizeStreamingMarkdown(stripThinkTags(streamingContent))}
{normalizeStreamingMarkdown(
stripThinkTags(streamingContent),
)}
</Markdown>
</Chat.Message>
)}
Expand Down
57 changes: 37 additions & 20 deletions apps/agent/src/components/Chat/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ export default function ChatInput({
const [isUploading, setIsUploading] = useState(false);
const [isModeDropdownOpen, setIsModeDropdownOpen] = useState(false);
const [inputHeight, setInputHeight] = useState(CHAT_INPUT_MIN_HEIGHT);
const [isCustomScrollbarVisible, setIsCustomScrollbarVisible] = useState(false);
const [isCustomScrollbarVisible, setIsCustomScrollbarVisible] =
useState(false);
const [customScrollbarThumbTop, setCustomScrollbarThumbTop] = useState(
SCROLLBAR_TRACK_INSET,
);
const [customScrollbarThumbHeight, setCustomScrollbarThumbHeight] = useState(0);
const [customScrollbarThumbHeight, setCustomScrollbarThumbHeight] =
useState(0);
const hideScrollbarTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
null,
);
Expand Down Expand Up @@ -192,10 +194,7 @@ export default function ChatInput({
return;
}

const trackHeight = Math.max(
0,
viewportHeight - SCROLLBAR_TRACK_INSET * 2,
);
const trackHeight = Math.max(0, viewportHeight - SCROLLBAR_TRACK_INSET * 2);
if (trackHeight <= 0) return;

const thumbHeight = Math.max(
Expand All @@ -216,7 +215,8 @@ export default function ChatInput({
const getInputScrollableElement = (event?: unknown) => {
const eventLike = event as ScrollbarEventLike | undefined;
if (isHTMLElement(eventLike?.currentTarget)) return eventLike.currentTarget;
if (inputScrollableElementRef.current) return inputScrollableElementRef.current;
if (inputScrollableElementRef.current)
return inputScrollableElementRef.current;

if (typeof document === "undefined") return null;
const inputElement = document.querySelector(CHAT_INPUT_SELECTOR);
Expand All @@ -232,10 +232,7 @@ export default function ChatInput({
const contentHeight = toInputDimension(scrollElement.scrollHeight);
const viewportHeight = toInputDimension(scrollElement.clientHeight);

const trackHeight = Math.max(
1,
viewportHeight - SCROLLBAR_TRACK_INSET * 2,
);
const trackHeight = Math.max(1, viewportHeight - SCROLLBAR_TRACK_INSET * 2);
const thumbHeight = Math.max(
SCROLLBAR_MIN_THUMB_HEIGHT,
Math.min(trackHeight, (viewportHeight / contentHeight) * trackHeight),
Expand All @@ -253,7 +250,8 @@ export default function ChatInput({
scrollElement: HTMLElement;
scrollTop?: number;
}) => {
const { contentHeight, viewportHeight } = getInputScrollMetrics(scrollElement);
const { contentHeight, viewportHeight } =
getInputScrollMetrics(scrollElement);
const normalizedScrollTop = Math.max(0, scrollTop);

inputScrollTopRef.current = normalizedScrollTop;
Expand All @@ -265,7 +263,10 @@ export default function ChatInput({
};

const restoreDocumentUserSelect = () => {
if (typeof document === "undefined" || restoreUserSelectRef.current === null) {
if (
typeof document === "undefined" ||
restoreUserSelectRef.current === null
) {
return;
}
document.body.style.userSelect = restoreUserSelectRef.current;
Expand Down Expand Up @@ -316,12 +317,16 @@ export default function ChatInput({

cancelEventSelection(event);

const { maxThumbOffset, maxScrollTop } =
getInputScrollMetrics(inputScrollableElement);
const { maxThumbOffset, maxScrollTop } = getInputScrollMetrics(
inputScrollableElement,
);
const deltaY = pageY - dragState.startPageY;
const nextScrollTop = Math.min(
maxScrollTop,
Math.max(0, dragState.startScrollTop + (deltaY / maxThumbOffset) * maxScrollTop),
Math.max(
0,
dragState.startScrollTop + (deltaY / maxThumbOffset) * maxScrollTop,
),
);

inputScrollableElement.scrollTop = nextScrollTop;
Expand Down Expand Up @@ -528,7 +533,10 @@ export default function ChatInput({
</Text>
</Pressable>

<View ref={modeDropdownContainerRef} style={styles.modeSelectorContainer}>
<View
ref={modeDropdownContainerRef}
style={styles.modeSelectorContainer}
>
<Pressable
style={[
styles.modeSelectorTrigger,
Expand All @@ -544,7 +552,10 @@ export default function ChatInput({
>
<Text
numberOfLines={1}
style={[styles.modeSelectorTriggerText, { color: colors.text }]}
style={[
styles.modeSelectorTriggerText,
{ color: colors.text },
]}
>
{activeMode.title}
</Text>
Expand Down Expand Up @@ -615,12 +626,18 @@ export default function ChatInput({
style={[
styles.iconChip,
{
backgroundColor: isOpen ? colors.backgroundFlat : colors.card,
backgroundColor: isOpen
? colors.backgroundFlat
: colors.card,
},
]}
onPress={() => setIsOpen((o) => !o)}
>
<ToolsIcon stroke={colors.placeholder} width={16} height={16} />
<ToolsIcon
stroke={colors.placeholder}
width={16}
height={16}
/>
</Pressable>
)}
>
Expand Down
Loading
Loading