Skip to content

feat(mastracode): auth routing fix, tool injection, and auth storage init#13611

Merged
DanielSLew merged 28 commits into
mastra-ai:mainfrom
superset-sh:kitenite/jet
Mar 4, 2026
Merged

feat(mastracode): auth routing fix, tool injection, and auth storage init#13611
DanielSLew merged 28 commits into
mastra-ai:mainfrom
superset-sh:kitenite/jet

Conversation

@Kitenite
Copy link
Copy Markdown
Contributor

@Kitenite Kitenite commented Feb 28, 2026

Summary

This PR combines two efforts: auth routing fixes and external tool injection / auth storage init.

Auth routing fix (was #13611)

Fixes Anthropic/OpenAI auth resolution by making AuthStorage credential type (api_key vs oauth) the authoritative signal for model routing and provider access checks. Previously, API-key auth could be routed through OAuth bearer-token paths, causing 401 Invalid bearer token errors.

External tool injection & auth storage init (was #13561)

  • Export createAuthStorage() for standalone auth provider initialization
  • Support extraTools (static or function) and HookManager pre/post hooks in createDynamicTools
  • Rename imagesfiles in harness sendMessage to use AI SDK FilePart shape
  • Apply disabledTools filtering to subagent tool maps to prevent bypass through delegation

Changes

  • mastracode/src/agents/model.ts — Direct OpenAI API-key model path; resolve auth via AuthStorage
  • mastracode/src/agents/tools.tsToolLike interface, wrapToolWithHooks, function-form extraTools
  • mastracode/src/index.tscreateAuthStorage export, filterDisabled for subagent tools, typed config
  • mastracode/src/providers/claude-max.ts, openai-codex.tssetAuthStorage accepts undefined
  • mastracode/src/tui/ — Provider access checks aligned with stored credential type
  • packages/core/src/harness/harness.tsfiles instead of images, warn on non-string file data
  • packages/core/src/harness/types.tsfile variant in HarnessMessageContent

Validation

pnpm --filter ./mastracode exec vitest run \
  src/__tests__/create-auth-storage.test.ts \
  src/agents/__tests__/tools.test.ts \
  src/agents/__tests__/extra-tools.test.ts \
  src/agents/__tests__/model.test.ts \
  src/tui/commands/__tests__/models-pack.test.ts \
  src/tui/__tests__/mastra-tui-hooks.test.ts \
  --project unit:mastracode --isolate
# 6 files passed, 57 tests passed

Closes #13561

🤖 Generated with Claude Code

Kitenite and others added 7 commits February 27, 2026 14:49
* feat(harness): rename images→files and use AI SDK FilePart shape

- sendMessage now accepts `files` (matching AI SDK FilePart: data, mediaType, filename)
  instead of `images` (custom shape with mimeType field mismatch)
- Mapping is now a direct passthrough instead of pointless field rename
- Add `type: 'file'` to HarnessMessageContent union for generic file parts
- Handle `file` parts in convertToHarnessMessage for history reload

* fix(mastracode): remap images→files in TUI fireMessage

The harness sendMessage param was renamed from `images` (custom shape)
to `files` (AI SDK FilePart shape). Remap at the TUI boundary so
clipboard paste still works.
- Add afterEach cleanup in auth storage test for isolation
- Skip malformed file parts (non-string data) instead of silently coercing
- Add changesets for mastracode (minor) and @mastra/core (patch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 28, 2026

🦋 Changeset detected

Latest commit: 87c626f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
mastracode Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 28, 2026

@Kitenite is attempting to deploy a commit to the Mastra Team on Vercel.

A member of the Team first needs to authorize it.

fix(mastracode): use auth storage as source of truth for anthropic/openai auth
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds OpenAI API-key path and getOpenAIApiKey(), exports createAuthStorage() and wires credential-driven auth, wraps tools with pre/post hooks and disabled-tools filtering, renames harness message parts from imagesfiles, adds a fork-sync GitHub Action, and expands related tests.

Changes

Cohort / File(s) Summary
Changesets & Docs
/.changeset/huge-boxes-travel.md, /.changeset/real-wolves-thank.md, /.changeset/thin-walls-bet.md, README.md
Added changelog entries and README notices for migrations, new API, and fork-sync workflow.
CI Workflow
.github/workflows/sync-upstream-fork.yml
New scheduled/manual workflow to sync forks with upstream default branch, with safety checks and early-exit when no upstream commits.
Auth API & Bootstrapping
mastracode/src/index.ts, mastracode/src/__tests__/create-auth-storage.test.ts
Exported createAuthStorage(), wired shared AuthStorage into startup and providers, added disabledTools to config, and added tests for auth storage wiring.
Model resolution & OpenAI integration
mastracode/src/agents/model.ts, mastracode/src/agents/__tests__/model.test.ts
Imported OpenAI provider, added getOpenAIApiKey() export and openai API-key provider path, refactored Anthropic/OpenAI auth precedence, and updated tests.
Tooling hooks & disabled tools
mastracode/src/agents/tools.ts, mastracode/src/agents/__tests__/tools.test.ts, mastracode/src/agents/__tests__/extra-tools.test.ts
Added wrapToolWithHooks, extended createDynamicTools signature to accept hookManager and disabledTools, applied pre/post hook behavior and disabled-tools filtering; updated/added tests.
Providers: test keys & OAuth guard
mastracode/src/providers/claude-max.ts, mastracode/src/providers/openai-codex.ts
Fixed test branches to use constant 'test-api-key'; added guard in Claude Max OAuth path to reject api_key-typed stored credentials.
TUI: access checks & models pack
mastracode/src/tui/mastra-tui.ts, mastracode/src/tui/commands/models-pack.ts
Reworked access-level logic to read credentials from authStorage (oauth vs api_key), updated mappings, and auto-add providers that have configured API keys.
Harness & message payload
packages/core/src/harness/harness.ts, /.changeset/huge-boxes-travel.md
Documented and implemented migration from imagesfiles shape; harness now skips non-string file part data when composing message parts.
Tests & coverage
mastracode/src/agents/__tests__/*, mastracode/src/agents/model.test.ts, mastracode/src/agents/tools.test.ts
Expanded and adjusted tests for model resolution, tool hooks, disabled-tools filtering, and auth/storage semantics.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: authentication routing fixes and tool injection with auth storage initialization, which are the primary objectives of the PR.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
.changeset/thin-walls-bet.md (1)

1-2: Empty frontmatter — changeset may be unnecessary for infrastructure-only changes.

The YAML frontmatter has no packages listed. If this GitHub Action workflow doesn't affect any published package, a changeset file may not be needed at all since changesets are used to generate release notes for npm packages. Consider removing this file if no package changelog entries are required.

If a changeset is intentionally desired for documentation purposes, ensure the frontmatter explicitly declares at least one package with a change type (e.g., patch).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/thin-walls-bet.md around lines 1 - 2, The changeset file has
empty YAML frontmatter and likely isn't needed for infra-only changes; either
delete .changeset/thin-walls-bet.md if it doesn't affect any published package,
or replace the empty frontmatter with a proper entry that names the affected
package and a change type (e.g., add "packages/my-package: patch" or similar
frontmatter with a valid package id and "patch/minor/major") so the changeset
will be picked up for release notes.
mastracode/src/tui/commands/models-pack.ts (1)

472-484: Extract provider-access mapping into one shared helper.

This access-level block now duplicates buildProviderAccess in mastracode/src/tui/mastra-tui.ts. Centralizing it will prevent future drift in provider IDs/precedence (for example, openai-codex mapping).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mastracode/src/tui/commands/models-pack.ts` around lines 472 - 484, Extract
the duplicated provider-access logic into a single shared helper by
creating/using a common function (e.g., buildProviderAccess) instead of the
local accessLevel/access implementation; replace the local const accessLevel and
const access in models-pack.ts with a call to the shared buildProviderAccess
helper so provider ID-to-access precedence (like 'openai-codex') is centralized
and reuseable across mastracode/src/tui/mastra-tui.ts and this file, preserving
the same return types (ProviderAccessLevel/ProviderAccess) and behavior for
providers anthropic, openai, cerebras, google, and deepseek.
mastracode/src/agents/model.ts (1)

129-145: You can simplify duplicate Anthropic API-key resolution.

storedCred is already read in this branch, so calling getAnthropicApiKey() immediately afterward re-checks the same source with equivalent logic.

♻️ Optional simplification
   } else if (isAnthropicModel) {
     const bareModelId = modelId.substring('anthropic/'.length);
     const storedCred = authStorage.get('anthropic');

     // Primary path: explicit OAuth credential
     if (storedCred?.type === 'oauth') {
       return opencodeClaudeMaxProvider(bareModelId);
     }

-    // Secondary path: explicit stored API key credential
-    if (storedCred?.type === 'api_key' && storedCred.key.trim().length > 0) {
-      return anthropicApiKeyProvider(bareModelId, storedCred.key.trim());
-    }
-
-    // Fallback: direct API key from AuthStorage
-    const apiKey = getAnthropicApiKey();
+    const apiKey = storedCred?.type === 'api_key' ? storedCred.key.trim() : undefined;
     if (apiKey) {
       return anthropicApiKeyProvider(bareModelId, apiKey);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mastracode/src/agents/model.ts` around lines 129 - 145, The branch
redundantly re-reads Anthropic API key via getAnthropicApiKey() after already
fetching storedCred; change the logic to use storedCred.key when
storedCred?.type === 'api_key' (trim and validate) and only call
getAnthropicApiKey() when storedCred is undefined/not present, then pass that
key into anthropicApiKeyProvider(bareModelId, key) or return
opencodeClaudeMaxProvider(bareModelId) for storedCred?.type === 'oauth'; update
the block using the existing symbols storedCred, getAnthropicApiKey,
anthropicApiKeyProvider, opencodeClaudeMaxProvider and bareModelId so you no
longer duplicate the same storage lookup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/real-wolves-thank.md:
- Around line 2-8: Rewrite the changeset to be outcome-focused and
package-scoped: for the mastracode entry describe the developer-visible outcomes
(e.g., "mastracode now runs tool executions with configurable pre/post hooks via
HookManager" and "developers can initialize auth storage directly using exported
createAuthStorage for standalone auth providers"), remove internal dependency
bookkeeping language, and ensure the `@mastra/core` frontmatter is justified by
stating that the release includes a patch-level peer dependency bump required by
mastracode (or any consumer-facing API change in `@mastra/core` if applicable);
make sure the narrative only describes effects relevant to the packages listed
in the frontmatter and adjust the frontmatter if any package mentioned does not
actually change.

---

Nitpick comments:
In @.changeset/thin-walls-bet.md:
- Around line 1-2: The changeset file has empty YAML frontmatter and likely
isn't needed for infra-only changes; either delete .changeset/thin-walls-bet.md
if it doesn't affect any published package, or replace the empty frontmatter
with a proper entry that names the affected package and a change type (e.g., add
"packages/my-package: patch" or similar frontmatter with a valid package id and
"patch/minor/major") so the changeset will be picked up for release notes.

In `@mastracode/src/agents/model.ts`:
- Around line 129-145: The branch redundantly re-reads Anthropic API key via
getAnthropicApiKey() after already fetching storedCred; change the logic to use
storedCred.key when storedCred?.type === 'api_key' (trim and validate) and only
call getAnthropicApiKey() when storedCred is undefined/not present, then pass
that key into anthropicApiKeyProvider(bareModelId, key) or return
opencodeClaudeMaxProvider(bareModelId) for storedCred?.type === 'oauth'; update
the block using the existing symbols storedCred, getAnthropicApiKey,
anthropicApiKeyProvider, opencodeClaudeMaxProvider and bareModelId so you no
longer duplicate the same storage lookup.

In `@mastracode/src/tui/commands/models-pack.ts`:
- Around line 472-484: Extract the duplicated provider-access logic into a
single shared helper by creating/using a common function (e.g.,
buildProviderAccess) instead of the local accessLevel/access implementation;
replace the local const accessLevel and const access in models-pack.ts with a
call to the shared buildProviderAccess helper so provider ID-to-access
precedence (like 'openai-codex') is centralized and reuseable across
mastracode/src/tui/mastra-tui.ts and this file, preserving the same return types
(ProviderAccessLevel/ProviderAccess) and behavior for providers anthropic,
openai, cerebras, google, and deepseek.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9257d01 and 0444fb0.

📒 Files selected for processing (17)
  • .changeset/huge-boxes-travel.md
  • .changeset/real-wolves-thank.md
  • .changeset/thin-walls-bet.md
  • .github/workflows/sync-upstream-fork.yml
  • README.md
  • mastracode/src/__tests__/create-auth-storage.test.ts
  • mastracode/src/agents/__tests__/model.test.ts
  • mastracode/src/agents/__tests__/tools.test.ts
  • mastracode/src/agents/model.ts
  • mastracode/src/agents/tools.ts
  • mastracode/src/index.ts
  • mastracode/src/providers/claude-max.ts
  • mastracode/src/providers/openai-codex.ts
  • mastracode/src/tui/commands/models-pack.ts
  • mastracode/src/tui/mastra-tui.ts
  • packages/core/src/harness/harness.ts
  • packages/core/src/harness/types.ts

Comment thread .changeset/real-wolves-thank.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/harness/harness.ts`:
- Around line 1317-1323: The code in harness.ts currently drops a part when
part.data is not a string; update the handling around the conditional that
builds the file object (the branch that does content.push({ type: 'file', data:
part.data, mediaType: ... })) to detect and convert known binary shapes instead
of silently skipping: accept ArrayBuffer/Uint8Array/Buffer and convert them to a
base64 or UTF-8 string as appropriate (or wrap binary payloads with a clear
binary marker), only skip when the shape is truly unsupported, and ensure
mediaType is preserved; modify the logic around part.data checks and the
content.push call to perform these conversions and fallback logging/error for
unsupported types.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0444fb0 and 267442f.

📒 Files selected for processing (2)
  • mastracode/src/agents/tools.ts
  • packages/core/src/harness/harness.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • mastracode/src/agents/tools.ts

Comment on lines +1317 to 1323
if (typeof part.data !== 'string') {
break;
}
content.push({
type: 'file',
data: typeof part.data === 'string' ? part.data : '',
data: part.data,
mediaType:
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.

⚠️ Potential issue | 🟡 Minor

Avoid silently dropping non-string file payloads.

Line 1317 currently skips the part entirely when part.data is not a string. That can lose file content in history for binary-backed file parts. Prefer converting known binary types and only skipping truly unsupported shapes.

Suggested patch
-        case 'file':
-          if (typeof part.data !== 'string') {
-            break;
-          }
-          content.push({
-            type: 'file',
-            data: part.data,
+        case 'file': {
+          let fileData: string | undefined;
+          if (typeof part.data === 'string') {
+            fileData = part.data;
+          } else if (part.data instanceof Uint8Array) {
+            fileData = Buffer.from(part.data).toString('base64');
+          } else if (part.data instanceof ArrayBuffer) {
+            fileData = Buffer.from(new Uint8Array(part.data)).toString('base64');
+          }
+          if (!fileData) {
+            break;
+          }
+          content.push({
+            type: 'file',
+            data: fileData,
             mediaType:
               (part as { mediaType?: string }).mediaType ??
               (part as { mimeType?: string }).mimeType ??
               'application/octet-stream',
             ...((part as { filename?: string }).filename ? { filename: (part as { filename?: string }).filename } : {}),
           });
-          break;
+          break;
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (typeof part.data !== 'string') {
break;
}
content.push({
type: 'file',
data: typeof part.data === 'string' ? part.data : '',
data: part.data,
mediaType:
case 'file': {
let fileData: string | undefined;
if (typeof part.data === 'string') {
fileData = part.data;
} else if (part.data instanceof Uint8Array) {
fileData = Buffer.from(part.data).toString('base64');
} else if (part.data instanceof ArrayBuffer) {
fileData = Buffer.from(new Uint8Array(part.data)).toString('base64');
}
if (!fileData) {
break;
}
content.push({
type: 'file',
data: fileData,
mediaType:
(part as { mediaType?: string }).mediaType ??
(part as { mimeType?: string }).mimeType ??
'application/octet-stream',
...((part as { filename?: string }).filename ? { filename: (part as { filename?: string }).filename } : {}),
});
break;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/harness/harness.ts` around lines 1317 - 1323, The code in
harness.ts currently drops a part when part.data is not a string; update the
handling around the conditional that builds the file object (the branch that
does content.push({ type: 'file', data: part.data, mediaType: ... })) to detect
and convert known binary shapes instead of silently skipping: accept
ArrayBuffer/Uint8Array/Buffer and convert them to a base64 or UTF-8 string as
appropriate (or wrap binary payloads with a clear binary marker), only skip when
the shape is truly unsupported, and ensure mediaType is preserved; modify the
logic around part.data checks and the content.push call to perform these
conversions and fallback logging/error for unsupported types.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mastracode/src/agents/tools.ts`:
- Around line 22-60: The wrapToolWithHooks and createDynamicTools APIs use broad
any types allowing invalid tool shapes at compile time; define a proper Tool
interface (e.g., interface Tool { execute(input: unknown, context?: unknown):
Promise<unknown> | unknown; [key: string]: unknown }) and replace usages of any:
change wrapToolWithHooks(toolName: string, tool: Tool, hookManager?:
HookManager): Tool and type extraTools as Record<string, Tool>; ensure
hookManager is typed as HookManager | undefined and adjust internal variables
(output, preResult) to use appropriate types from the Tool and HookManager
contracts so TypeScript enforces the execute method shape instead of allowing
any.

In `@mastracode/src/index.ts`:
- Around line 56-57: The subagent path is not applying the disabledTools filter,
so models can still reach tools like execute_command, write_file, and
string_replace_lsp via subagent; update the code that builds the subagent tool
maps (the code that registers/returns tools for subagent execution) to apply the
same disabledTools filtering logic used by createDynamicTools: when constructing
the subagent tool map, remove any tool whose key/name appears in disabledTools
(not just the three hardcoded names) and reuse the same predicate or helper used
by createDynamicTools to keep behavior consistent across both main and subagent
tool sets.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 267442f and 96f7767.

📒 Files selected for processing (3)
  • mastracode/src/agents/__tests__/extra-tools.test.ts
  • mastracode/src/agents/tools.ts
  • mastracode/src/index.ts

Comment thread mastracode/src/agents/tools.ts Outdated
Comment thread mastracode/src/index.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
mastracode/src/index.ts (1)

122-129: ⚠️ Potential issue | 🟠 Major

Apply disabledTools filtering to subagent tool maps too.

Line 128 applies disabledTools only to the main dynamic toolset. Subagent maps (Lines 168-175) still expose tool entries directly, so disabled tools can be reached through delegation.

Suggested fix
+  const disabledToolSet = new Set(config?.disabledTools ?? []);
+  const filterDisabledTools = <T extends Record<string, unknown>>(toolMap: T): Partial<T> =>
+    Object.fromEntries(Object.entries(toolMap).filter(([name]) => !disabledToolSet.has(name))) as Partial<T>;
@@
-  const readOnlyTools = {
+  const readOnlyTools = filterDisabledTools({
     view: viewTool,
     search_content: grepTool,
     find_files: globTool,
-  };
+  });
@@
-      tools: {
+      tools: {
         ...readOnlyTools,
-        string_replace_lsp: stringReplaceLspTool,
-        write_file: writeFileTool,
-        execute_command: executeCommandTool,
+        ...filterDisabledTools({
+          string_replace_lsp: stringReplaceLspTool,
+          write_file: writeFileTool,
+          execute_command: executeCommandTool,
+        }),
         task_write: taskWriteTool,
         task_check: taskCheckTool,
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mastracode/src/index.ts` around lines 122 - 129, The main Agent is correctly
created with createDynamicTools(..., config?.disabledTools) but the subagent
tool maps are not filtered, allowing disabled tools to be reachable via
delegation; update the code that builds subagent tool maps (where subagents are
constructed—look for the code that creates per-subagent tool maps around the
subagent creation block) to call the same filtering logic (reuse
createDynamicTools or the internal filter used by createDynamicTools) and pass
config?.disabledTools so each subagent's tools explicitly exclude disabled
entries; ensure both the main Agent instantiation (Agent) and all subagent tool
map constructions use the same disabledTools parameter.
🧹 Nitpick comments (2)
mastracode/src/agents/__tests__/model.test.ts (1)

197-233: Consider adding whitespace-trimming key tests for both key getters.

Both getters trim keys before returning; adding one case like ' sk-key ' (and optionally whitespace-only) would lock that behavior down.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mastracode/src/agents/__tests__/model.test.ts` around lines 197 - 233, Add
unit tests for getAnthropicApiKey and getOpenAIApiKey that verify they trim
surrounding whitespace and handle whitespace-only values: mock
mockAuthStorageInstance.get to return { type: 'api_key', key: '  sk-trimmed-key 
' } and assert the getters return 'sk-trimmed-key', and also mock a
whitespace-only key like '   ' and assert the getters return undefined; place
these new cases alongside the existing tests for getAnthropicApiKey and
getOpenAIApiKey to lock down trimming behavior.
mastracode/src/agents/model.ts (1)

154-163: Remove redundant Anthropic API-key fallback check.

Line 147 already reads storedCred after authStorage.reload(), and Lines 155-157 already handle the api_key case. The getAnthropicApiKey() fallback at Line 160 currently re-checks the same condition.

Refactor
-    // Fallback: direct API key from AuthStorage
-    const apiKey = getAnthropicApiKey();
-    if (apiKey) {
-      return anthropicApiKeyProvider(bareModelId, apiKey);
-    }
     // No auth configured — attempt OAuth provider which will prompt login
     return opencodeClaudeMaxProvider(bareModelId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mastracode/src/agents/model.ts` around lines 154 - 163, The fallback call to
getAnthropicApiKey() is redundant because storedCred (reloaded from authStorage)
is already checked for type 'api_key' and handled by anthropicApiKeyProvider;
remove the fallback block that reads getAnthropicApiKey() and returns
anthropicApiKeyProvider(bareModelId, apiKey). Keep the existing storedCred check
(storedCred?.type === 'api_key') and its call to anthropicApiKeyProvider; if you
intended a true fallback for non-stored credentials, instead change the logic to
only call getAnthropicApiKey() when storedCred is null/undefined before invoking
anthropicApiKeyProvider.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@mastracode/src/index.ts`:
- Around line 122-129: The main Agent is correctly created with
createDynamicTools(..., config?.disabledTools) but the subagent tool maps are
not filtered, allowing disabled tools to be reachable via delegation; update the
code that builds subagent tool maps (where subagents are constructed—look for
the code that creates per-subagent tool maps around the subagent creation block)
to call the same filtering logic (reuse createDynamicTools or the internal
filter used by createDynamicTools) and pass config?.disabledTools so each
subagent's tools explicitly exclude disabled entries; ensure both the main Agent
instantiation (Agent) and all subagent tool map constructions use the same
disabledTools parameter.

---

Nitpick comments:
In `@mastracode/src/agents/__tests__/model.test.ts`:
- Around line 197-233: Add unit tests for getAnthropicApiKey and getOpenAIApiKey
that verify they trim surrounding whitespace and handle whitespace-only values:
mock mockAuthStorageInstance.get to return { type: 'api_key', key: ' 
sk-trimmed-key  ' } and assert the getters return 'sk-trimmed-key', and also
mock a whitespace-only key like '   ' and assert the getters return undefined;
place these new cases alongside the existing tests for getAnthropicApiKey and
getOpenAIApiKey to lock down trimming behavior.

In `@mastracode/src/agents/model.ts`:
- Around line 154-163: The fallback call to getAnthropicApiKey() is redundant
because storedCred (reloaded from authStorage) is already checked for type
'api_key' and handled by anthropicApiKeyProvider; remove the fallback block that
reads getAnthropicApiKey() and returns anthropicApiKeyProvider(bareModelId,
apiKey). Keep the existing storedCred check (storedCred?.type === 'api_key') and
its call to anthropicApiKeyProvider; if you intended a true fallback for
non-stored credentials, instead change the logic to only call
getAnthropicApiKey() when storedCred is null/undefined before invoking
anthropicApiKeyProvider.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96f7767 and fde9ce8.

📒 Files selected for processing (7)
  • mastracode/src/agents/__tests__/extra-tools.test.ts
  • mastracode/src/agents/__tests__/model.test.ts
  • mastracode/src/agents/model.ts
  • mastracode/src/agents/tools.ts
  • mastracode/src/index.ts
  • mastracode/src/tui/mastra-tui.ts
  • packages/core/src/harness/harness.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • mastracode/src/agents/tests/extra-tools.test.ts
  • mastracode/src/agents/tools.ts
  • packages/core/src/harness/harness.ts

@abhiaiyer91
Copy link
Copy Markdown
Member

@Kitenite please address coderabbit comments

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
mastracode/src/index.ts (1)

63-64: ⚠️ Potential issue | 🟠 Major

Apply disabledTools filtering to subagent tool maps too.

disabledTools is only enforced via createDynamicTools (Line 130). Subagent maps still expose unfiltered tools, so hard-disabled tools can be reached through subagent execution.

Suggested patch
+  const disabledToolSet = new Set(config?.disabledTools ?? []);
+  const filterDisabledTools = <T extends Record<string, unknown>>(toolMap: T) =>
+    Object.fromEntries(Object.entries(toolMap).filter(([name]) => !disabledToolSet.has(name)));

-  const readOnlyTools = {
+  const readOnlyTools = filterDisabledTools({
     view: viewTool,
     search_content: grepTool,
     find_files: globTool,
-  };
+  });

@@
       tools: {
         ...readOnlyTools,
-        string_replace_lsp: stringReplaceLspTool,
-        write_file: writeFileTool,
-        execute_command: executeCommandTool,
-        task_write: taskWriteTool,
-        task_check: taskCheckTool,
+        ...filterDisabledTools({
+          string_replace_lsp: stringReplaceLspTool,
+          write_file: writeFileTool,
+          execute_command: executeCommandTool,
+          task_write: taskWriteTool,
+          task_check: taskCheckTool,
+        }),
       },

Also applies to: 124-132

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mastracode/src/index.ts` around lines 63 - 64, The subagent tool maps are not
being filtered by disabledTools (filtering only happens in createDynamicTools),
so update the code that builds subagent tool maps (e.g., wherever subagent tool
map construction or a function like createSubagentToolMaps / buildSubagentTools
is defined) to apply the same disabledTools filtering logic: either
call/createDynamicTools for each subagent or reuse the exact predicate that
removes tools present in disabledTools when assembling subagentMaps, handle the
optional/undefined disabledTools safely, and add/update tests to ensure tools
listed in disabledTools are not present in any subagent map.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@mastracode/src/index.ts`:
- Around line 63-64: The subagent tool maps are not being filtered by
disabledTools (filtering only happens in createDynamicTools), so update the code
that builds subagent tool maps (e.g., wherever subagent tool map construction or
a function like createSubagentToolMaps / buildSubagentTools is defined) to apply
the same disabledTools filtering logic: either call/createDynamicTools for each
subagent or reuse the exact predicate that removes tools present in
disabledTools when assembling subagentMaps, handle the optional/undefined
disabledTools safely, and add/update tests to ensure tools listed in
disabledTools are not present in any subagent map.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fde9ce8 and 4e9f428.

📒 Files selected for processing (2)
  • mastracode/src/index.ts
  • packages/core/src/harness/harness.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/harness/harness.ts

@Kitenite Kitenite changed the title fix(mastracode): align anthropic/openai auth routing with stored credential type feat(mastracode): auth routing fix, external tool injection, and auth storage init Mar 3, 2026
- Apply disabledTools filtering to subagent tool maps so disabled tools
  can't be bypassed through subagent delegation
- Replace broad `any` types with ToolLike interface in tools.ts
- Add console.warn for non-string file payloads in harness stream parser
- Remove invalid changeset with empty frontmatter (infra-only change)
- Accept undefined in setAuthStorage to enable clean test teardown
- Remove `as any` casts in create-auth-storage test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Kitenite Kitenite changed the title feat(mastracode): auth routing fix, external tool injection, and auth storage init fix(mastracode): address PR review comments Mar 3, 2026
@Kitenite Kitenite changed the title fix(mastracode): address PR review comments feat(mastracode): auth routing fix, tool injection, and auth storage init Mar 3, 2026
Kitenite and others added 2 commits March 3, 2026 15:08
- Merged upstream's function-form extraTools support with ToolLike types
- Fixed upstream test that asserted on workspace-provided tool (view)
  instead of a createDynamicTools tool (web_search)
- Removed duplicate changeset (huge-boxes-travel.md)
- Updated real-wolves-thank.md to be mastracode-scoped only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
// Test environment: use API key
if (process.env.NODE_ENV === 'test' || process.env.VITEST) {
const anthropic = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY || 'test-api-key',
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Btw lmk if this is not ok, it was picking up real api keys in my test so i removed it

Remove README fork note and sync-upstream-fork workflow — these are
fork-specific and should not be upstreamed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread README.md Outdated

Information about the project setup can be found in the [development documentation](./DEVELOPMENT.md)

For fork maintainers: this repository includes a daily GitHub Action at `.github/workflows/sync-upstream-fork.yml` that syncs your fork's default branch from `mastra-ai/mastra`.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gonna remove this real quick

@@ -0,0 +1,52 @@
name: Sync Fork With Upstream
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removing this

Comment thread mastracode/src/__tests__/create-auth-storage.test.ts Outdated
Kitenite and others added 4 commits March 4, 2026 10:08
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep ToolLike type from PR and request_access rename from upstream.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@DanielSLew DanielSLew merged commit f6b91c4 into mastra-ai:main Mar 4, 2026
40 of 43 checks passed
@Kitenite Kitenite deleted the kitenite/jet branch March 4, 2026 22:54
wardpeet pushed a commit that referenced this pull request Mar 9, 2026
…init (#13611)

Co-authored-by: Satya Patel <satyapatel111@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Roamin <97863888+roaminro@users.noreply.github.com>
Co-authored-by: Daniel Lew <51924260+DanielSLew@users.noreply.github.com>
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.

5 participants