Skip to content

Commit 2476e86

Browse files
committed
Merge remote-tracking branch 'origin/main' into test-ui-fixes
2 parents 84b8901 + 7d86a94 commit 2476e86

49 files changed

Lines changed: 10575 additions & 2700 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codex/review-prompt.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Read these files before reviewing:
99
1. **`pr-diff.patch`** — The PR diff (generated at runtime). This is the primary input.
1010
2. **`AGENTS.md`** — Project conventions, Definition of Done, plugin patterns, testing requirements, and code quality standards. This is the source of truth for how code in this project should look.
1111

12-
You may read other files in the repository to understand surrounding context (e.g., how a modified function is called, what a referenced constant is). However, all review comments must target lines that appear in the diff.
12+
You may read other files in the repository **only** to understand how code changed in the diff is called or referenced. Do not review, comment on, or mention code in files or packages that are not part of the diff. All review comments and the summary must be strictly scoped to changes introduced by this PR's diff — nothing else.
1313

1414
## Review Philosophy
1515

@@ -26,19 +26,21 @@ When both exist, report blockers first.
2626

2727
Do three passes:
2828

29-
1. **Context + risk-map pass (mandatory)**Read full touched files (not only hunk lines) and identify high-risk zones: platform/runtime boundaries, event handlers, async cleanup (`finally`), auth/validation boundaries, and repeated predicates/guards.
29+
1. **Context + risk-map pass (mandatory)**Start from diff hunks, then read surrounding or full touched files when needed to evaluate maintainability, coupling, naming, and extraction opportunities. Use this context to assess changed behavior, not to run unrelated file-wide audits.
3030
2. **Blockers pass** — Scan for correctness bugs, security issues, API/schema contract breaks, missing migrations, data integrity risks, and missing tests for changed behavior. These are `🔴 Bug` comments.
3131
3. **Maintainability pass** — Scan for code bloat, readability issues, naming problems, pattern violations, hardcoded values, and architecture drift in touched areas. These are `🟡 Issue`, `🔵 Nit`, or `💡 Suggestion` comments.
3232

3333
### Comment Gate
3434

35-
Before posting any comment, verify all three conditions:
35+
Before posting any comment, verify all four conditions:
3636

3737
1. **Introduced by this diff** — The issue is introduced or materially worsened by the changes in this PR, not pre-existing.
3838
2. **Materially impactful** — The issue affects correctness, security, readability, or maintainability in a meaningful way. Not a theoretical concern.
3939
3. **Concrete fix direction** — You can suggest a specific fix or clear direction. If you can only say "this seems off" without a concrete suggestion, do not comment.
40+
4. **Scope fit** — If the issue is mainly in pre-existing code, the PR must touch the same function/module and fixing it must directly simplify, de-risk, or de-duplicate the new/changed code.
4041

4142
If any check fails, skip the comment.
43+
Every comment must be traceable to changed behavior in this PR and anchored to a right-side line present in `pr-diff.patch`. Prefer added/modified lines; use nearby unchanged hunk lines only when necessary to explain a directly related issue.
4244

4345
**Uncertainty guard:** If you are not certain an issue is real and cannot verify it from the diff and allowed context, do not label it `🔴 Bug`. Downgrade to `🟡 Issue` or `💡 Suggestion`, or skip it entirely.
4446

@@ -53,29 +55,34 @@ If any check fails, skip the comment.
5355
- Logic errors, off-by-one, null/undefined handling, incorrect assumptions, race conditions.
5456
- Boundary conditions — empty arrays, null inputs, zero values, maximum values.
5557
- Error handling — swallowed errors, missing error propagation, unhelpful error messages. Do not flag missing error handling for internal code that cannot reasonably fail.
58+
- Streaming/multipart handlers — verify a request cannot send multiple responses (e.g., multi-file parts triggering repeated `res.json()` calls). If a route expects one file, ensure parser limits and single-response guards exist.
5659
- Unsafe runtime assumptions hidden by type assertions (`as ...`, `as any`, non-null `!`) when values come from events, external I/O, or platform-specific APIs.
5760
- Platform/runtime compatibility assumptions — usage of globals/APIs (`window`, `document`, `Node`, `process`, browser-only APIs) in cross-runtime code paths must be guarded.
5861

5962
#### Security
6063

64+
6165
- Injection risks (SQL, command, XSS) when handling user input.
6266
- Hardcoded secrets — API keys, passwords, tokens in code.
6367
- Missing input validation at system boundaries (user input, external APIs). Not for internal function calls.
6468
- Auth bypass, privilege escalation, or missing authorization checks.
69+
- Filesystem path confinement — when IDs/paths come from requests, verify storage layers enforce root containment via resolved-path checks; do not rely only on caller-side sanitization.
6570

6671
#### API Compatibility
6772

6873
- Breaking changes to API response schemas or status codes without migration path.
6974
- Removed or renamed API endpoints, query parameters, or response fields that existing consumers depend on.
7075
- Database schema changes that require migration or backfill.
7176
- MCP tool signature changes (renamed tools, changed input schemas) that break existing clients.
77+
- HTTP status semantics — ensure client/input errors are 4xx and unexpected internal failures are 5xx; blanket 400 handling in catch-all paths is a correctness/API contract issue.
7278

7379
#### Tests for Changed Behavior
7480

7581
- New behavior must have corresponding tests covering core functionality and error handling.
7682
- Bug fixes must include a regression test that would have caught the original bug.
7783
- Changed behavior must have updated tests reflecting the new expectations.
7884
- If tests are present but brittle (testing implementation details rather than behavior), flag it.
85+
- For single-file upload endpoints, look for regression coverage of multi-file/malformed multipart inputs and confirm no double-response behavior.
7986
- Prefer tests that validate production behavior directly. If a test re-implements production decision logic locally, and could stay green while runtime behavior regresses, flag it and suggest importing shared runtime logic or testing via a higher-level behavior path.
8087

8188
Missing tests for changed behavior are blockers (`🔴 Bug`) only when the change affects user-facing behavior, API contracts, or data integrity. Missing tests for internal refactors or trivial changes are `🟡 Issue`.
@@ -113,6 +120,7 @@ Missing tests for changed behavior are blockers (`🔴 Bug`) only when the chang
113120
- **Wrong import paths in tests** — Tests importing from `src/` instead of `dist/`.
114121
- **Missing test categories** — Tests without "Core Functionality" and "Error Handling" describe blocks.
115122
- **Mixing concerns** — Route handlers doing business logic, database queries in API handlers, etc.
123+
- **Cross-provider behavior drift** — When multiple providers/implementations exist, verify shared options and output semantics behave consistently unless explicitly documented otherwise.
116124

117125
#### Hardcoded Values and Magic Constants
118126

@@ -135,7 +143,9 @@ Do not flag one-off numeric literals that are self-explanatory in context (e.g.,
135143
- Type annotations for code that already type-checks.
136144
- Things that are clearly intentional design choices backed by existing patterns.
137145
- Pre-existing issues in unchanged code outside the diff.
146+
- Pre-existing issues in touched files when the PR does not introduce/worsen them.
138147
- Adding documentation unless a public API is clearly undocumented.
148+
- Repository-wide or file-wide audits not required by the changed behavior.
139149

140150
## Comment Format
141151

@@ -174,4 +184,4 @@ The `line` field must refer to the line number in the new version of the file (r
174184

175185
## Summary
176186

177-
Write a brief (2–4 sentence) overall assessment in the `summary` field. Lead with blockers if any exist. Mention whether the PR is clean/minimal or has code quality issues. Include one sentence on maintainability direction in touched areas (improved / neutral / worsened, and why). If the PR looks good, say so.
187+
Write a brief (2–4 sentence) overall assessment in the `summary` field covering **only** what this PR's diff changes. Do not mention code, packages, or behavior outside the diff. Lead with blockers if any exist. Mention whether the PR is clean/minimal or has code quality issues. Include one sentence on maintainability direction in touched areas (improved / neutral / worsened, and why). If the PR looks good, say so.

.github/workflows/codex-review.yml

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ jobs:
7575
}
7676
);
7777
78-
// Build set of valid (path:line) pairs from diff patches
78+
// Build set of valid (path:line) pairs from right-side diff hunk lines
79+
// (added + context). This keeps comments bound to changed areas.
7980
const validLines = new Set();
8081
for (const file of files) {
8182
// Skip binary/large/truncated files with no patch
@@ -89,20 +90,26 @@ jobs:
8990
currentLine = parseInt(hunkMatch[1], 10);
9091
continue;
9192
}
92-
// Deleted lines don't exist in the new file
93-
if (line.startsWith('-')) continue;
94-
// Context lines and added lines are valid targets
95-
if (!line.startsWith('\\')) {
93+
// Added lines are valid comment targets
94+
if (line.startsWith('+')) {
9695
validLines.add(`${file.filename}:${currentLine}`);
9796
currentLine++;
97+
continue;
9898
}
99+
// Deleted lines don't exist in the new file
100+
if (line.startsWith('-')) continue;
101+
// Ignore hunk metadata lines
102+
if (line.startsWith('\\')) continue;
103+
// Context lines on the right side are also valid targets
104+
validLines.add(`${file.filename}:${currentLine}`);
105+
currentLine++;
99106
}
100107
}
101108
102-
// Partition comments into valid (on diff lines) and overflow
109+
// Partition comments into valid (on right-side diff lines) and dropped
103110
const comments = Array.isArray(review.comments) ? review.comments : [];
104111
const validComments = [];
105-
const overflowComments = [];
112+
const droppedComments = [];
106113
107114
for (const comment of comments) {
108115
const key = `${comment.path}:${comment.line}`;
@@ -114,19 +121,13 @@ jobs:
114121
side: 'RIGHT',
115122
});
116123
} else {
117-
overflowComments.push(comment);
124+
droppedComments.push(comment);
118125
}
119126
}
120127
121-
// Build review body: summary + any overflow comments
128+
// Build review body from summary only.
129+
// Intentionally do NOT publish out-of-diff comments.
122130
let body = review.summary || 'Codex review complete.';
123-
if (overflowComments.length > 0) {
124-
body += '\n\n### Additional comments\n';
125-
body += '_These comments target lines outside the diff and could not be posted inline._\n\n';
126-
for (const c of overflowComments) {
127-
body += `- **\`${c.path}:${c.line}\`** — ${c.body}\n`;
128-
}
129-
}
130131
131132
// Post the review
132133
await github.rest.pulls.createReview({
@@ -138,4 +139,4 @@ jobs:
138139
comments: validComments,
139140
});
140141
141-
console.log(`Review posted: ${validComments.length} inline comments, ${overflowComments.length} overflow comments`);
142+
console.log(`Review posted: ${validComments.length} inline comments, ${droppedComments.length} dropped out-of-diff comments`);

apps/agent/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
"better-sqlite3": "^12.2.0",
5656
"dkg.js": "^8.2.2",
5757
"dotenv": "^16.3.1",
58-
"mysql2": "^3.6.5",
5958
"drizzle-orm": "^0.44.7",
6059
"expo": "53.0.20",
6160
"expo-blur": "~14.1.5",
@@ -77,6 +76,7 @@
7776
"expo-three": "^8.0.0",
7877
"expo-web-browser": "~14.2.0",
7978
"js-sha256": "^0.11.1",
79+
"mysql2": "^3.6.5",
8080
"nodemailer": "^7.0.6",
8181
"prompts": "^2.4.2",
8282
"react": "19.0.0",

apps/agent/src/server/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,14 @@ const version = "1.0.0";
3232
const { oauthPlugin, openapiSecurityScheme } = createOAuthPlugin({
3333
storage: new SqliteOAuthStorageProvider(db),
3434
issuerUrl: new URL(process.env.EXPO_PUBLIC_MCP_URL),
35-
scopesSupported: ["mcp", "llm", "scope123", "blob"],
35+
scopesSupported: [
36+
"mcp",
37+
"llm",
38+
"scope123",
39+
"blob",
40+
"epcis.read",
41+
"epcis.write",
42+
],
3643
loginPageUrl: new URL(process.env.EXPO_PUBLIC_APP_URL + "/login"),
3744
schema: userCredentialsSchema,
3845
async login(credentials) {
@@ -101,6 +108,12 @@ const app = createPluginServer({
101108
oauthPlugin,
102109
(_, __, api) => {
103110
api.use("/mcp", authorized(["mcp"]));
111+
api.use("/mcp", (req, res, next) => {
112+
if (res.locals.auth) {
113+
(req as any).auth = res.locals.auth;
114+
}
115+
next();
116+
});
104117
api.use("/llm", authorized(["llm"]));
105118
api.use("/blob", authorized(["blob"]));
106119
api.use("/change-password", authorized([]));

apps/agent/src/server/scripts/setup.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import {
99
import {
1010
getLLMProviderApiKeyEnvName,
1111
LLMProvider,
12-
DEFAULT_SYSTEM_PROMPT,
1312
} from "@/shared/chat";
13+
import { DEFAULT_SYSTEM_PROMPT } from "@/shared/prompts/defaultSystemPrompt";
1414

1515
async function setup() {
1616
const r = await prompts([
@@ -50,6 +50,25 @@ async function setup() {
5050
initial: DEFAULT_SYSTEM_PROMPT,
5151
format: (val) => (val === DEFAULT_SYSTEM_PROMPT ? "" : val.trim()),
5252
},
53+
{
54+
type: "select",
55+
name: "docConversionProvider",
56+
message: "Document conversion provider",
57+
choices: [
58+
{ title: "unpdf — basic PDF only", value: "unpdf" },
59+
{ title: "Mistral OCR — complex PDF/DOCX/PPTX", value: "mistral" },
60+
],
61+
initial: 0,
62+
},
63+
{
64+
type: (_, a) =>
65+
a.docConversionProvider === "mistral" && a.llmProvider !== "mistralai"
66+
? "text"
67+
: null,
68+
name: "mistralApiKey",
69+
message: "MISTRAL_API_KEY",
70+
validate: (val) => val.length || "Required for Mistral OCR provider",
71+
},
5372
{
5473
type: "select",
5574
name: "dkgEnv",
@@ -132,8 +151,9 @@ async function setup() {
132151
{
133152
type: "text",
134153
name: "dbFilename",
135-
message: "Database filename (i.e: example.db)",
154+
message: "Database filename (e.g. example.db)",
136155
validate: (val) => val.length || "Required",
156+
format: (val) => (val.endsWith(".db") ? val : `${val}.db`),
137157
},
138158
]);
139159

@@ -158,7 +178,8 @@ SMTP_USER="${r.smtpUsername || ""}"
158178
SMTP_PASS="${r.smtpPassword || ""}"
159179
SMTP_SECURE=${r.smtpSecure === undefined ? "true" : r.smtpSecure}
160180
SMTP_FROM="${r.smtpFrom || ""}"
161-
`,
181+
DOCUMENT_CONVERSION_PROVIDER="${r.docConversionProvider}"
182+
${r.docConversionProvider === "mistral" && r.llmProvider !== "mistralai" ? `MISTRAL_API_KEY="${r.mistralApiKey}"\n` : ""}`,
162183
);
163184

164185
console.log("Creating .env.development.local file...");

apps/agent/src/shared/chat.ts

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type {
99
import type { ToolCallChunk } from "@langchain/core/messages/tool";
1010
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
1111

12+
import { DEFAULT_SYSTEM_PROMPT } from "./prompts/defaultSystemPrompt";
13+
1214
export type { ToolDefinition };
1315
export type ToolInfo = {
1416
name: string;
@@ -339,7 +341,7 @@ export const processStreamingCompletion = async (
339341
try {
340342
args = tc.args ? JSON.parse(tc.args) : {};
341343
} catch {
342-
// Malformed JSON from partial streaming send raw
344+
// Malformed JSON from partial streaming - send raw
343345
args = {};
344346
}
345347
toolCalls.push({
@@ -358,17 +360,17 @@ export const processStreamingCompletion = async (
358360
writeSSE(res, { event: "done", data: {} });
359361
} catch (streamError) {
360362
if (hasSentContent) {
361-
// Partial content was already sent don't re-invoke and risk
363+
// Partial content was already sent - don't re-invoke and risk
362364
// duplicated/mixed output. Send an error so the UI can recover.
363365
writeSSE(res, {
364366
event: "error",
365367
data: {
366368
message:
367-
"Stream interrupted please retry your message",
369+
"Stream interrupted - please retry your message",
368370
},
369371
});
370372
} else {
371-
// No content sent yet safe to fallback to a full invoke
373+
// No content sent yet - safe to fallback to a full invoke
372374
try {
373375
const result = await provider.invoke(messages, options);
374376
const content = result.content;
@@ -514,42 +516,9 @@ export const makeStreamingCompletionRequest = async (
514516

515517
// Stream ended without an explicit done/error event (server crash, network drop)
516518
if (!streamFinalized) {
517-
callbacks.onError("Connection lost the server stopped responding");
519+
callbacks.onError("Connection lost - the server stopped responding");
518520
}
519521
} finally {
520522
reader.releaseLock();
521523
}
522524
};
523-
524-
export const DEFAULT_SYSTEM_PROMPT = `
525-
You are a DKG Agent that helps users interact with the OriginTrail Decentralized Knowledge Graph (DKG) using available Model Context Protocol (MCP) tools.
526-
Your role is to help users create, retrieve, and analyze verifiable knowledge in a friendly, approachable, and knowledgeable way, making the technology accessible to both experts and non-experts. When replying, use markdown (e.g. bold text, bullet points, tables, etc.) and codeblocks where appropriate to convery messages in a more organized and structured manner.
527-
528-
## Core Responsibilities
529-
- Answer Questions: Retrieve and explain knowledge from the DKG to help users understand and solve problems.
530-
- Create Knowledge Assets: Assist users in publishing new knowledge assets to the DKG using MCP tools.
531-
- Perform Analyses: Use DKG data and MCP tools to perform structured analyses, presenting results clearly.
532-
- Be Helpful and Approachable: Communicate in simple, user-friendly terms. Use analogies and clear explanations where needed, but avoid unnecessary technical jargon unless requested.
533-
534-
## Privacy Rule (IMPORTANT)
535-
When creating or publishing knowledge assets:
536-
- If privacy is explicitly specified, follow the user’s instruction.
537-
- If privacy is NOT specified, ALWAYS set privacy to "private".
538-
- NEVER default to "public" without explicit user consent.
539-
This ensures sensitive information is not unintentionally exposed.
540-
541-
## Interaction Guidelines
542-
1. Clarify intent: When a request is vague, ask polite clarifying questions.
543-
2. Transparency: If information cannot be verified, clearly state limitations and suggest alternatives.
544-
3. Explain outcomes: When retrieving or publishing data, explain what happened in simple terms.
545-
4. Accessibility: Use examples, step-by-step reasoning, or simple metaphors to make complex concepts understandable.
546-
5. Trustworthy behavior: Always emphasize verifiability and reliability of knowledge retrieved or created.
547-
548-
## Examples of Behavior
549-
- User asks to publish knowledge without specifying privacy → Agent publishes with "privacy": "private" and explains:
550-
"I’ve published this knowledge privately so only you (or authorized parties) can access it. If you’d like it public, just let me know."
551-
552-
- User asks to retrieve knowledge → Agent uses MCP retrieval tools and explains results in a simple, structured way.
553-
554-
- User asks a complex analytical question → Agent retrieves relevant knowledge from the DKG, performs the analysis, and presents results in a clear format (e.g., list, table, etc.).
555-
`.trim();

0 commit comments

Comments
 (0)