Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions .changeset/fix-connector-tool-mode-gating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openwiki": patch
---

Gate OpenWiki connector tools by run mode. `createOpenWikiConnectorTools` now takes an `outputMode` and returns no connector tools for `repository` (code) mode, so code-mode agents are no longer offered credentialed external ingestion tools (Gmail, Slack, X, ...) that throw on missing credentials. Personal/local-wiki runs are unchanged. Fixes #444.
2 changes: 1 addition & 1 deletion src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ function createOpenWikiAgentGraph(

return createDeepAgent({
model: options.model,
tools: createOpenWikiConnectorTools(),
tools: createOpenWikiConnectorTools(options.outputMode),
checkpointer: options.checkpointer,
backend,
middleware:
Expand Down
14 changes: 13 additions & 1 deletion src/connectors/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
import { constants as fsConstants } from "node:fs";
import { lstat, open, readdir, stat } from "node:fs/promises";
import path from "node:path";
import type { OpenWikiOutputMode } from "../agent/types.js";
import {
getConnectorConfigPath,
getConnectorRawDir,
Expand All @@ -20,7 +21,18 @@ import {
} from "./mcp-runtime.js";
import type { ConnectorId, ConnectorIngestOptions } from "./types.js";

export function createOpenWikiConnectorTools(): StructuredToolInterface[] {
export function createOpenWikiConnectorTools(
outputMode: OpenWikiOutputMode = "local-wiki",
): StructuredToolInterface[] {
// Connector tools perform credentialed external fetches (Gmail, Slack, X, ...)
// and write raw data under the OpenWiki home. They are a personal/local-wiki
// capability: a code-mode run documents a codebase and must never be handed
// connector ingestion, which otherwise throws on missing credentials and
// wastes tokens discovering sources it has no business touching. See #444.
if (outputMode === "repository") {
return [];
}

return [
new DynamicStructuredTool({
name: "openwiki_list_connectors",
Expand Down
54 changes: 51 additions & 3 deletions test/raw-connector-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,37 @@ describe("raw connector tools", () => {
});
});

describe("connector tool run-mode gating (#444)", () => {
test("personal/local-wiki mode exposes connector ingest tools", async () => {
const home = await createTempHome();
const tools = await loadConnectorToolsForMode(home, "local-wiki");

expect(tools.map((tool) => tool.name)).toContain(
"openwiki_ingest_connector",
);
expect(tools.map((tool) => tool.name)).toContain(
"openwiki_ingest_all_connectors",
);
});

test("code/repository mode is not offered any connector tools", async () => {
const home = await createTempHome();
const tools = await loadConnectorToolsForMode(home, "repository");

expect(tools).toEqual([]);
});

test("defaulting without a mode behaves like local-wiki", async () => {
const home = await createTempHome();
const tools = await loadConnectorTools(home);

expect(tools.length).toBeGreaterThan(0);
expect(tools.map((tool) => tool.name)).toContain(
"openwiki_ingest_connector",
);
});
});

interface RawItemsResult {
files: string[];
latestFiles: string[];
Expand All @@ -134,13 +165,30 @@ interface RawReadResult {
async function loadConnectorTools(
home: string,
): Promise<StructuredToolInterface[]> {
const { createOpenWikiConnectorTools } =
await loadConnectorToolsModule(home);

return createOpenWikiConnectorTools();
}

async function loadConnectorToolsForMode(
home: string,
outputMode: "local-wiki" | "repository",
): Promise<StructuredToolInterface[]> {
const { createOpenWikiConnectorTools } =
await loadConnectorToolsModule(home);

return createOpenWikiConnectorTools(outputMode);
}

async function loadConnectorToolsModule(
home: string,
): Promise<typeof import("../src/connectors/tools.ts")> {
vi.resetModules();
process.env.HOME = home;
process.env.USERPROFILE = home;
const { createOpenWikiConnectorTools } =
await import("../src/connectors/tools.ts");

return createOpenWikiConnectorTools();
return import("../src/connectors/tools.ts");
}

function getTool(
Expand Down