Skip to content

feat(observability): add Obsidian context source with 24 MCP tools - #3077

Merged
diegosouzapw merged 8 commits into
cheaper-inference:release/v3.8.9from
branben:obsidian-context-source
Jun 3, 2026
Merged

diegosouzapw merged 8 commits into
cheaper-inference:release/v3.8.9from
branben:obsidian-context-source

Conversation

@branben

@branben branben commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Obsidian as a context source via the Local REST API community plugin, providing 24 MCP tools scoped under read:obsidian and write:obsidian.

This follows the same pattern established by the Notion context source PR.

Requirements

⚠️ Obsidian Local REST API community plugin must be installed and running in the user's vault (default port 127.0.0.1:27125). The API token is configured in OmniRoute Settings → Context Sources.

What's included

  • 24 MCP tools — search, read, write, and sync operations against the user's vault
  • Full sync server — bidirectional desktop ↔ mobile vault sync with conflict detection/resolution
  • obsidian-plugin/ — community plugin for the sync server
  • Error handling — auth errors, 404s, server errors, timeouts with exponential backoff
  • DB config — per-API-key Obsidian endpoint and token configuration

Notes

  • Working on a custom community plugin that utilizes the Tailscale integration already built into OmniRoute
  • Considering future integration with obsidian-second-brain workflows

Brandon Bennett and others added 5 commits May 30, 2026 18:38
…to all dynamic tool definitions

- Move !enforceScopes guard before MCP_TOOL_MAP lookup in evaluateToolScopes()
- Add inlineScopes parameter for dynamic tool scope resolution
- Add scopes to all 33 dynamic tool definitions across 5 tool files
- Wire toolDef.scopes through withScopeEnforcement in server.ts
- Preserves existing behavior: tool_definition_missing returned when
  enforceScopes=true and no scopes found anywhere
fix: evaluateToolScopes guard order and add scopes to all dynamic MCP tools
feat: Notion MCP context source (search, read, query, write Notion via routed models)
@branben
branben requested a review from diegosouzapw as a code owner June 2, 2026 20:24

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces Notion as an MCP context source, adding six new tools, a REST API client, token persistence, settings endpoints, and a dashboard UI card, alongside fixing scope enforcement for dynamic tool groups. The review feedback highlights several critical and high-severity issues: the token persistence function incorrectly uses INSERT OR IGNORE which prevents token updates; the scope enforcement logic blocks tools that require no scopes; the settings API saves the token before validating it, risking overwriting a valid token; the Notion API client leaks timeout resources and misclassifies aborted requests; the block-appending tool silently truncates payloads instead of enforcing the 100-block limit via Zod; the documentation contains discrepancies regarding tool names; and the new UI card hardcodes English strings instead of utilizing the instantiated translation hook.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/db/notion.ts
Comment on lines +22 to +31
export function setNotionToken(token: string): void {
try {
const db = getDbInstance();
db.prepare(
"INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES (?, ?, ?)"
).run(NOTION_NAMESPACE, NOTION_TOKEN_KEY, JSON.stringify(token));
} catch {
// Non-fatal — token still works in-memory if persistence fails.
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

Using INSERT OR IGNORE prevents updating the Notion token if one already exists in the database. When a user changes their token in the settings UI, the database will silently ignore the insert and keep the old token. Use INSERT OR REPLACE to allow updating the token correctly.

Suggested change
export function setNotionToken(token: string): void {
try {
const db = getDbInstance();
db.prepare(
"INSERT OR IGNORE INTO key_value (namespace, key, value) VALUES (?, ?, ?)"
).run(NOTION_NAMESPACE, NOTION_TOKEN_KEY, JSON.stringify(token));
} catch {
// Non-fatal — token still works in-memory if persistence fails.
}
}
export function setNotionToken(token: string): void {
try {
const db = getDbInstance();
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES (?, ?, ?)"
).run(NOTION_NAMESPACE, NOTION_TOKEN_KEY, JSON.stringify(token));
} catch {
// Non-fatal.
}
}

Comment on lines +111 to 122
const toolScopes = inlineScopes ?? MCP_TOOL_MAP[toolName]?.scopes;
const required = Array.isArray(toolScopes) ? Array.from(toolScopes) : [];

if (required.length === 0) {
return {
allowed: false,
required: [],
provided: Array.from(callerScopes),
provided,
missing: [],
reason: "tool_definition_missing",
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

If a tool requires no scopes (i.e., scopes is an empty array []), required.length will be 0. Under the current logic, this returns allowed: false with reason: "tool_definition_missing", which blocks any public or scope-free tools. We should check if the tool definition actually exists to distinguish between a missing tool and a tool that requires no scopes.

  const toolDef = MCP_TOOL_MAP[toolName];
  const toolScopes = inlineScopes ?? toolDef?.scopes;

  if (!inlineScopes && !toolDef) {
    return {
      allowed: false,
      required: [],
      provided,
      missing: [],
      reason: "tool_definition_missing",
    };
  }

  const required = Array.isArray(toolScopes) ? Array.from(toolScopes) : [];

  if (!enforceScopes || required.length === 0) {
    return { allowed: true, required, provided, missing: [] };
  }

Comment on lines +51 to +72
try {
setNotionToken(parsed.data.token);

const client = createNotionClient(parsed.data.token);
const result = await client.searchPagesAndDatabases("test", undefined, 1);
if (result && typeof result === "object" && "object" in result && (result as Record<string, unknown>).object === "error") {
clearNotionToken();
return NextResponse.json(
{ error: "Token validation failed: invalid token", connected: false },
{ status: 400 }
);
}

return NextResponse.json({
connected: true,
message: "Notion integration token saved and validated",
});
} catch (error) {
clearNotionToken();
const msg = error instanceof Error ? error.message : String(error);
return NextResponse.json({ error: msg, connected: false }, { status: 400 });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The POST handler saves the token to the database before validating it, and deletes it if validation fails. This means if the user had a valid token and entered an invalid one, the existing valid token is lost. It is much safer to validate the token first, and only save it to the database if validation succeeds.

  try {
    const client = createNotionClient(parsed.data.token);
    const result = await client.searchPagesAndDatabases("test", undefined, 1);
    if (result && typeof result === "object" && "object" in result && (result as Record<string, unknown>).object === "error") {
      return NextResponse.json(
        { error: "Token validation failed: invalid token", connected: false },
        { status: 400 }
      );
    }

    setNotionToken(parsed.data.token);

    return NextResponse.json({
      connected: true,
      message: "Notion integration token saved and validated",
    });
  } catch (error) {
    const msg = error instanceof Error ? error.message : String(error);
    return NextResponse.json({ error: msg, connected: false }, { status: 400 });
  }

Comment thread src/lib/notion/api.ts
Comment on lines +99 to +154
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${apiKey}`,
"Notion-Version": NOTION_VERSION,
"Content-Type": "application/json",
...(options.headers as Record<string, string>),
},
signal: mergedSignal,
});

if (!response.ok) {
const body = await response.json().catch(() => ({})) as Record<string, unknown>;
const errBody = body as Partial<NotionErrorBody>;
const code = errBody?.code ?? "unknown";
const msg = errBody?.message ?? `HTTP ${response.status}`;
const error = classifyNotionError(response.status, code, msg);

if (error instanceof NotionRateLimitError) {
lastError = error;
const waitMs = error.retryAfter * 1000 + Math.pow(2, attempt) * 200;
await sleep(waitMs);
continue;
}

if (error instanceof NotionServerError && attempt < MAX_RETRIES - 1) {
lastError = error;
await sleep(Math.pow(2, attempt) * 500);
continue;
}

throw error;
}

return response.json();
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
clearTimeout(timeout);
throw new NotionTimeoutError("Notion API request timed out after 55s");
}
if (err instanceof NotionAuthError || err instanceof NotionNotFoundError || err instanceof NotionValidationError) {
clearTimeout(timeout);
throw err;
}
if (attempt < MAX_RETRIES - 1) {
lastError = err instanceof Error ? err : new NotionServerError(String(err));
await sleep(Math.pow(2, attempt) * 500);
continue;
}
}
}

clearTimeout(timeout);
throw lastError ?? new NotionServerError("Exhausted all retries");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

There are two issues here:

  1. Resource Leak: clearTimeout(timeout) is not called when the fetch succeeds and returns response.json(). This leaks the timeout in the event loop.
  2. AbortError Misclassification: If the caller aborts the request via options.signal, it throws an AbortError, which is caught and re-thrown as a NotionTimeoutError saying "Notion API request timed out after 55s". This is incorrect and misleading.

Wrapping the loop in a try...finally block ensures the timeout is always cleared, and checking controller.signal.aborted allows distinguishing between a timeout and a caller abort.

  try {
    for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
      try {
        const response = await fetch(url, {
          ...options,
          headers: {
            Authorization: `Bearer ${apiKey}`,
            "Notion-Version": NOTION_VERSION,
            "Content-Type": "application/json",
            ...(options.headers as Record<string, string>),
          },
          signal: mergedSignal,
        });

        if (!response.ok) {
          const body = await response.json().catch(() => ({})) as Record<string, unknown>;
          const errBody = body as Partial<NotionErrorBody>;
          const code = errBody?.code ?? "unknown";
          const msg = errBody?.message ?? `HTTP ${response.status}`;
          const error = classifyNotionError(response.status, code, msg);

          if (error instanceof NotionRateLimitError) {
            lastError = error;
            const waitMs = error.retryAfter * 1000 + Math.pow(2, attempt) * 200;
            await sleep(waitMs);
            continue;
          }

          if (error instanceof NotionServerError && attempt < MAX_RETRIES - 1) {
            lastError = error;
            await sleep(Math.pow(2, attempt) * 500);
            continue;
          }

          throw error;
        }

        return await response.json();
      } catch (err) {
        if (err instanceof Error && err.name === "AbortError") {
          if (controller.signal.aborted) {
            throw new NotionTimeoutError("Notion API request timed out after 55s");
          }
          throw err;
        }
        if (err instanceof NotionAuthError || err instanceof NotionNotFoundError || err instanceof NotionValidationError) {
          throw err;
        }
        if (attempt < MAX_RETRIES - 1) {
          lastError = err instanceof Error ? err : new NotionServerError(String(err));
          await sleep(Math.pow(2, attempt) * 500);
          continue;
        }
      }
    }
    throw lastError ?? new NotionServerError("Exhausted all retries");
  } finally {
    clearTimeout(timeout);
  }

Comment on lines +97 to +101
blockId: z.string().min(1).describe("Target block or page ID to append to"),
children: z.array(z.unknown()).describe("Array of block objects to append"),
after: z.string().optional().describe("Block ID to append after (position parameter)"),
}),
handler: async (args: { blockId: string; children: unknown[]; after?: string }) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The Notion API has a strict limit of 100 blocks per request when appending blocks. In src/lib/notion/api.ts, the array is silently sliced to 100 elements, which can lead to silent data loss/truncation. We should enforce this limit in the Zod schema so that the caller gets a clear validation error if they exceed 100 blocks.

    inputSchema: z.object({
      blockId: z.string().min(1).describe("Target block or page ID to append to"),
      children: z.array(z.unknown()).max(100).describe("Array of block objects to append (max 100)"),
      after: z.string().optional().describe("Block ID to append after (position parameter)"),
    }),

Comment on lines +197 to +204
| Tool | Scopes | Description |
| :--------------------------- | :--------------- | :------------------------------------------------------------------------------------ |
| `omniroute_notion_search` | `read:notion` | Full-text search across all pages and databases |
| `omniroute_notion_list_databases` | `read:notion` | List all accessible databases with schema metadata |
| `omniroute_notion_get_database` | `read:notion` | Get database schema by ID |
| `omniroute_notion_query_database` | `read:notion` | Query a database with filters, sorts, and pagination |
| `omniroute_notion_read` | `read:notion` | Read a page or block by ID with its content |
| `omniroute_notion_append_blocks` | `write:notion`| Append children blocks to a parent block (max 100 per request) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The documentation table for the Notion Context Source has a few discrepancies:

  1. It lists omniroute_notion_list_databases and omniroute_notion_read, but these tools do not exist in notionTools.ts. Instead, notionTools.ts defines notion_get_page and notion_list_block_children.
  2. The tool names in the table are prefixed with omniroute_ (e.g., omniroute_notion_search), but they are registered in server.ts using their raw names (e.g., notion_search).
Suggested change
| Tool | Scopes | Description |
| :--------------------------- | :--------------- | :------------------------------------------------------------------------------------ |
| `omniroute_notion_search` | `read:notion` | Full-text search across all pages and databases |
| `omniroute_notion_list_databases` | `read:notion` | List all accessible databases with schema metadata |
| `omniroute_notion_get_database` | `read:notion` | Get database schema by ID |
| `omniroute_notion_query_database` | `read:notion` | Query a database with filters, sorts, and pagination |
| `omniroute_notion_read` | `read:notion` | Read a page or block by ID with its content |
| `omniroute_notion_append_blocks` | `write:notion`| Append children blocks to a parent block (max 100 per request) |
| Tool | Scopes | Description |
| :--------------------------- | :--------------- | :------------------------------------------------------------------------------------ |
| `notion_search` | `read:notion` | Full-text search across all pages and databases |
| `notion_get_page` | `read:notion` | Get the content and metadata of a Notion page by its ID |
| `notion_list_block_children` | `read:notion` | List all block children of a Notion block or page |
| `notion_get_database` | `read:notion` | Get metadata and schema of a Notion database by its ID |
| `notion_query_database` | `read:notion` | Query a Notion database with filters, sorts, and pagination |
| `notion_append_blocks` | `write:notion` | Append block children to an existing Notion block or page (max 100 per request) |

Comment on lines +97 to +106
<div className="flex items-center gap-2 flex-wrap">
<span className="font-semibold text-sm">Notion</span>
<Badge variant={connected ? "success" : "default"}>
{connected ? "Connected" : "Not connected"}
</Badge>
</div>
<p className="text-xs text-text-muted mt-0.5">
Search, read, query, and write to Notion through routed AI models
</p>
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The component instantiates const t = useTranslations("endpoint"); but does not use it for any of the user-facing strings. All strings are hardcoded in English, violating the project's internationalization standards. Use the t translation function for these strings.

Suggested change
<div className="flex items-center gap-2 flex-wrap">
<span className="font-semibold text-sm">Notion</span>
<Badge variant={connected ? "success" : "default"}>
{connected ? "Connected" : "Not connected"}
</Badge>
</div>
<p className="text-xs text-text-muted mt-0.5">
Search, read, query, and write to Notion through routed AI models
</p>
</div>
<div className="flex items-center gap-2 flex-wrap">
<span className="font-semibold text-sm">Notion</span>
<Badge variant={connected ? "success" : "default"}>
{connected ? t("connected") : t("notConnected")}
</Badge>
</div>
<p className="text-xs text-text-muted mt-0.5">
{t("notionDescription")}
</p>
</div>

Adds Obsidian as a context source via the Local REST API community plugin.
24 MCP tools scoped under read:obsidian / write:obsidian covering search,
read, write, vault management, and bidirectional sync.

- open-sse/mcp-server/tools/obsidianTools.ts — 24 tool definitions
- src/lib/obsidian/api.ts — REST client with retry, timeout, error classification
- src/lib/db/obsidian.ts — token/URL/vault_path persistence in key_value table
- src/lib/obsidianSync.ts — WebDAV sync helpers
- src/app/api/settings/obsidian/route.ts — GET/POST/DELETE settings API
- ObsidianSourceCard.tsx — dashboard UI card
- obsidian-plugin/ — community plugin for desktop↔mobile vault sync
- 6 test files (unit + integration), 52+ tests

Fixes: INSERT OR IGNORE → INSERT OR REPLACE in setObsidianToken() to allow
token updates. Uses relative paths in install-obsidian-plugin.sh.

Co-Authored-By: Claude <noreply@anthropic.com>
@branben
branben marked this pull request as draft June 3, 2026 05:13
@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.9 June 3, 2026 09:34
…a removal

- Integrates 24 Obsidian MCP tools (read/write/search/sync) via Local REST API plugin
- Adds read:obsidian and write:obsidian scopes
- Adds DB modules for Obsidian and Notion configuration
- Adds REST endpoints: /api/settings/obsidian and /api/settings/notion
- Adds ObsidianSourceCard.tsx and NotionSourceCard.tsx UI components
- Removes .serena/ from repo tracking (added to .gitignore — local-only AI tool config)
- Resolves conflicts with release/v3.8.9: keeps agentSkillTools registration + adds obsidianTools
- Add skillRegistry/skillExecutor imports + define skillToMcpToolName inline
- Remove missing apiKeyContextSources import in db/obsidian.ts (stub per-key fallback)
- Add missing getDbInstance import in src/lib/obsidian/api.ts
@diegosouzapw
diegosouzapw marked this pull request as ready for review June 3, 2026 09:45
@diegosouzapw
diegosouzapw merged commit 9db306e into cheaper-inference:release/v3.8.9 Jun 3, 2026
2 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thank you @branben for this excellent addition! The Obsidian context source with 24 MCP tools follows the same pattern as the Notion integration and is a great addition to OmniRoute. Adjustments applied before merging: (1) removed .serena/ AI tool config from the repo (added to .gitignore), (2) resolved import conflicts with the release branch while preserving agentSkillTools, (3) fixed missing module imports (getDbInstance in obsidian/api.ts, stub for apiKeyContextSources). Merged into release/v3.8.9 — will ship in v3.8.9!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9892e3f9eb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1199 to +1201
// ── Notion Context Source Tools ───────────────
notionTools.forEach((toolDef) => {
server.registerTool(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove duplicate Notion tool registration

When createMcpServer() runs, this second notionTools.forEach registers every Notion tool name after the earlier Notion block at lines 1145-1170 already registered the same names. The MCP SDK rejects duplicate tool names during registration, so starting the MCP server now fails before any tools (including the new Obsidian tools) are usable.

Useful? React with 👍 / 👎.

import { useTranslations } from "next-intl";
import { Card, Button, Input, Badge } from "@/shared/components";

export default function ObsidianSourceCard() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mount the Obsidian card in Context Sources

The new settings UI is never reachable: a repo-wide rg "ObsidianSourceCard" only finds this component definition, while EndpointPageClient.tsx still imports/renders only NotionSourceCard in the Context Sources tab. In the documented Settings > Context Sources flow, users therefore cannot configure the Obsidian token from the UI and would have to call the API manually.

Useful? React with 👍 / 👎.

Comment on lines +125 to +128
const res = await fetch("/api/settings/obsidian/webdav", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vaultPath: vaultPath.trim() }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add the WebDAV settings route before calling it

When the WebDAV Enable button is used, this POST targets /api/settings/obsidian/webdav, but a repo-wide rg "settings/obsidian/webdav|api/v1/webdav" src/app open-sse src/lib shows only these client calls and helper code, with no Next route handler for the endpoint. The request will hit a 404 instead of calling enableObsidianVaultSync, so the advertised vault sync setup cannot be enabled from the UI.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9892e3f9eb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1199 to +1201
// ── Notion Context Source Tools ───────────────
notionTools.forEach((toolDef) => {
server.registerTool(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove duplicate Notion tool registration

When createMcpServer() runs, this second notionTools.forEach registers every Notion tool name after the earlier Notion block at lines 1145-1170 already registered the same names. The MCP SDK rejects duplicate tool names during registration, so starting the MCP server now fails before any tools (including the new Obsidian tools) are usable.

Useful? React with 👍 / 👎.

import { useTranslations } from "next-intl";
import { Card, Button, Input, Badge } from "@/shared/components";

export default function ObsidianSourceCard() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mount the Obsidian card in Context Sources

The new settings UI is never reachable: a repo-wide rg "ObsidianSourceCard" only finds this component definition, while EndpointPageClient.tsx still imports/renders only NotionSourceCard in the Context Sources tab. In the documented Settings > Context Sources flow, users therefore cannot configure the Obsidian token from the UI and would have to call the API manually.

Useful? React with 👍 / 👎.

Comment on lines +125 to +128
const res = await fetch("/api/settings/obsidian/webdav", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vaultPath: vaultPath.trim() }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add the WebDAV settings route before calling it

When the WebDAV Enable button is used, this POST targets /api/settings/obsidian/webdav, but a repo-wide rg "settings/obsidian/webdav|api/v1/webdav" src/app open-sse src/lib shows only these client calls and helper code, with no Next route handler for the endpoint. The request will hit a 404 instead of calling enableObsidianVaultSync, so the advertised vault sync setup cannot be enabled from the UI.

Useful? React with 👍 / 👎.

@diegosouzapw diegosouzapw mentioned this pull request Jun 3, 2026
diegosouzapw added a commit that referenced this pull request Jun 3, 2026
….ts (dynamic tools)

The Lint job's check:any-budget:t11 (string-blind /\bany\b/ regex) failed on:
- open-sse/executors/cursor.ts: the WORD 'any' in #3104's tool-commit/output-
  constraint prompt strings — zero real TS `any` in the file.
- open-sse/mcp-server/server.ts: 3 `(toolDef: any)` in dynamic memory/skill/
  compression tool-registration loops (#3077), guarded by existing @ts-ignore.
Both are v3.8.9-introduced and benign; set the per-file baseline to the count.
diegosouzapw added a commit that referenced this pull request Jun 4, 2026
…drift + #3100 dedup)

Full CI surfaced real failures that local subsets missed (gh-merged PRs bypass
the hooks that run these gates):
- typecheck:core (Lint job): 3 now-unused @ts-expect-error in mcp-server/server.ts
  (#3077 dynamic tool loops) → @ts-ignore (lenient, no TS2578).
- pack-artifact-policy.test.ts: build-reorg (#3124) renamed app/->dist/; the test
  still asserted app/ paths + REQUIRED order (it sorts alphabetically).
- electron-packaging.test.ts: extraResources from .next/electron-standalone ->
  .build/electron-standalone (#3124).
- glm-provider-model-import-route.test.ts: two GLM connections shared one apiKey,
  so #3100 (#3023) dedup collapsed them → only one discovery fetch. Distinct keys.

Remaining CI flakes (batch expiration, ModelSync self-fetch) pass in isolation —
concurrency/port flakiness under --test-concurrency=4, not real failures.
diegosouzapw added a commit that referenced this pull request Jun 4, 2026
…plugin e2e

- hasStandaloneAppBundle now accepts the legacy app/ bundle too (mirrors serve
  CLI's dist/->app/ fallback), fixing postinstall-support.test.ts after #3124.
- obsidian-plugin-e2e: #3077 committed the e2e test but NEVER committed its
  dependency obsidian-plugin/src/server.ts (un-ignored but unstaged) nor the
  'obsidian' npm pkg, so it crashed with ERR_MODULE_NOT_FOUND on every fresh
  checkout. Load the runtime values dynamically and skip the suite when absent
  (unit sync logic stays covered by obsidian-plugin-sync.test.ts).
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…heaper-inference#3077)

Integrated into release/v3.8.9. Fixes applied: removed .serena/ from repo (gitignored), resolved import conflicts preserving agentSkillTools, fixed TS errors.
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
….ts (dynamic tools)

The Lint job's check:any-budget:t11 (string-blind /\bany\b/ regex) failed on:
- open-sse/executors/cursor.ts: the WORD 'any' in cheaper-inference#3104's tool-commit/output-
  constraint prompt strings — zero real TS `any` in the file.
- open-sse/mcp-server/server.ts: 3 `(toolDef: any)` in dynamic memory/skill/
  compression tool-registration loops (cheaper-inference#3077), guarded by existing @ts-ignore.
Both are v3.8.9-introduced and benign; set the per-file baseline to the count.
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…drift + cheaper-inference#3100 dedup)

Full CI surfaced real failures that local subsets missed (gh-merged PRs bypass
the hooks that run these gates):
- typecheck:core (Lint job): 3 now-unused @ts-expect-error in mcp-server/server.ts
  (cheaper-inference#3077 dynamic tool loops) → @ts-ignore (lenient, no TS2578).
- pack-artifact-policy.test.ts: build-reorg (cheaper-inference#3124) renamed app/->dist/; the test
  still asserted app/ paths + REQUIRED order (it sorts alphabetically).
- electron-packaging.test.ts: extraResources from .next/electron-standalone ->
  .build/electron-standalone (cheaper-inference#3124).
- glm-provider-model-import-route.test.ts: two GLM connections shared one apiKey,
  so cheaper-inference#3100 (cheaper-inference#3023) dedup collapsed them → only one discovery fetch. Distinct keys.

Remaining CI flakes (batch expiration, ModelSync self-fetch) pass in isolation —
concurrency/port flakiness under --test-concurrency=4, not real failures.
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…plugin e2e

- hasStandaloneAppBundle now accepts the legacy app/ bundle too (mirrors serve
  CLI's dist/->app/ fallback), fixing postinstall-support.test.ts after cheaper-inference#3124.
- obsidian-plugin-e2e: cheaper-inference#3077 committed the e2e test but NEVER committed its
  dependency obsidian-plugin/src/server.ts (un-ignored but unstaged) nor the
  'obsidian' npm pkg, so it crashed with ERR_MODULE_NOT_FOUND on every fresh
  checkout. Load the runtime values dynamically and skip the suite when absent
  (unit sync logic stays covered by obsidian-plugin-sync.test.ts).
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…heaper-inference#3077)

Integrated into release/v3.8.9. Fixes applied: removed .serena/ from repo (gitignored), resolved import conflicts preserving agentSkillTools, fixed TS errors.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
….ts (dynamic tools)

The Lint job's check:any-budget:t11 (string-blind /\bany\b/ regex) failed on:
- open-sse/executors/cursor.ts: the WORD 'any' in cheaper-inference#3104's tool-commit/output-
  constraint prompt strings — zero real TS `any` in the file.
- open-sse/mcp-server/server.ts: 3 `(toolDef: any)` in dynamic memory/skill/
  compression tool-registration loops (cheaper-inference#3077), guarded by existing @ts-ignore.
Both are v3.8.9-introduced and benign; set the per-file baseline to the count.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…drift + cheaper-inference#3100 dedup)

Full CI surfaced real failures that local subsets missed (gh-merged PRs bypass
the hooks that run these gates):
- typecheck:core (Lint job): 3 now-unused @ts-expect-error in mcp-server/server.ts
  (cheaper-inference#3077 dynamic tool loops) → @ts-ignore (lenient, no TS2578).
- pack-artifact-policy.test.ts: build-reorg (cheaper-inference#3124) renamed app/->dist/; the test
  still asserted app/ paths + REQUIRED order (it sorts alphabetically).
- electron-packaging.test.ts: extraResources from .next/electron-standalone ->
  .build/electron-standalone (cheaper-inference#3124).
- glm-provider-model-import-route.test.ts: two GLM connections shared one apiKey,
  so cheaper-inference#3100 (cheaper-inference#3023) dedup collapsed them → only one discovery fetch. Distinct keys.

Remaining CI flakes (batch expiration, ModelSync self-fetch) pass in isolation —
concurrency/port flakiness under --test-concurrency=4, not real failures.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…plugin e2e

- hasStandaloneAppBundle now accepts the legacy app/ bundle too (mirrors serve
  CLI's dist/->app/ fallback), fixing postinstall-support.test.ts after cheaper-inference#3124.
- obsidian-plugin-e2e: cheaper-inference#3077 committed the e2e test but NEVER committed its
  dependency obsidian-plugin/src/server.ts (un-ignored but unstaged) nor the
  'obsidian' npm pkg, so it crashed with ERR_MODULE_NOT_FOUND on every fresh
  checkout. Load the runtime values dynamically and skip the suite when absent
  (unit sync logic stays covered by obsidian-plugin-sync.test.ts).
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…heaper-inference#3077)

Integrated into release/v3.8.9. Fixes applied: removed .serena/ from repo (gitignored), resolved import conflicts preserving agentSkillTools, fixed TS errors.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
….ts (dynamic tools)

The Lint job's check:any-budget:t11 (string-blind /\bany\b/ regex) failed on:
- open-sse/executors/cursor.ts: the WORD 'any' in cheaper-inference#3104's tool-commit/output-
  constraint prompt strings — zero real TS `any` in the file.
- open-sse/mcp-server/server.ts: 3 `(toolDef: any)` in dynamic memory/skill/
  compression tool-registration loops (cheaper-inference#3077), guarded by existing @ts-ignore.
Both are v3.8.9-introduced and benign; set the per-file baseline to the count.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…drift + cheaper-inference#3100 dedup)

Full CI surfaced real failures that local subsets missed (gh-merged PRs bypass
the hooks that run these gates):
- typecheck:core (Lint job): 3 now-unused @ts-expect-error in mcp-server/server.ts
  (cheaper-inference#3077 dynamic tool loops) → @ts-ignore (lenient, no TS2578).
- pack-artifact-policy.test.ts: build-reorg (cheaper-inference#3124) renamed app/->dist/; the test
  still asserted app/ paths + REQUIRED order (it sorts alphabetically).
- electron-packaging.test.ts: extraResources from .next/electron-standalone ->
  .build/electron-standalone (cheaper-inference#3124).
- glm-provider-model-import-route.test.ts: two GLM connections shared one apiKey,
  so cheaper-inference#3100 (cheaper-inference#3023) dedup collapsed them → only one discovery fetch. Distinct keys.

Remaining CI flakes (batch expiration, ModelSync self-fetch) pass in isolation —
concurrency/port flakiness under --test-concurrency=4, not real failures.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…plugin e2e

- hasStandaloneAppBundle now accepts the legacy app/ bundle too (mirrors serve
  CLI's dist/->app/ fallback), fixing postinstall-support.test.ts after cheaper-inference#3124.
- obsidian-plugin-e2e: cheaper-inference#3077 committed the e2e test but NEVER committed its
  dependency obsidian-plugin/src/server.ts (un-ignored but unstaged) nor the
  'obsidian' npm pkg, so it crashed with ERR_MODULE_NOT_FOUND on every fresh
  checkout. Load the runtime values dynamically and skip the suite when absent
  (unit sync logic stays covered by obsidian-plugin-sync.test.ts).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants