Skip to content

Prd 6236#2719

Draft
dimaMachina wants to merge 175 commits intoprd-6192-333from
prd-6236
Draft

Prd 6236#2719
dimaMachina wants to merge 175 commits intoprd-6192-333from
prd-6236

Conversation

@dimaMachina
Copy link
Collaborator

No description provided.

@vercel
Copy link

vercel bot commented Mar 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Mar 20, 2026 11:01pm
agents-docs Ready Ready Preview, Comment Mar 20, 2026 11:01pm
agents-manage-ui Error Error Mar 20, 2026 11:01pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: 310ecb4

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

This PR includes changesets to release 10 packages
Name Type
@inkeep/agents-manage-ui Patch
@inkeep/agents-core Patch
@inkeep/agents-sdk Patch
@inkeep/agents-api Patch
@inkeep/agents-cli Patch
@inkeep/agents-work-apps Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch
@inkeep/agents-email Patch
@inkeep/agents-mcp Patch

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

dimaMachina and others added 26 commits March 21, 2026 05:54
* fix: Make OpenTelemetry startup idempotent

* fix: Re-export defaultSDK and cache NodeSDK instance on globalThis

Restores the export on defaultSDK to avoid breaking the
create-agents-template subpath import. Moves the new NodeSDK()
construction behind a globalThis guard (getOrCreateSDK) so
repeated Vite HMR module evaluations reuse the same instance instead
of leaking fresh SDK objects.

Co-authored-by: mike-inkeep <mike-inkeep@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(template): use idempotent startOpenTelemetrySDK() in instrumentation

* fix: guard all OTel singletons behind globalThis for HMR idempotency

- Cache otlpExporter, batchProcessor, resource, instrumentations,
  spanProcessors, contextManager, and propagator on globalThis via
  Symbol keys and getOrCreate* helpers so HMR re-evaluation reuses
  existing instances instead of leaking new ones
- Make OtelGlobal type strict with per-key types, eliminating the
  loose `boolean | NodeSDK` union and the `as NodeSDK` cast
- Add logger.debug in the MetricReader catch block to distinguish
  clean idempotency from error-recovery idempotency
- Remove defaultSDK export (now module-private) since all consumers
  use startOpenTelemetrySDK() instead

* Fix type errors

* Simplify to just suppress the error since it's not an issue in prod, only local

* Limit to dev mode

* Add changeset for OTel HMR idempotency fix

Co-authored-by: Dimitri POSTOLOV <dimaMachina@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: mike-inkeep <mike-inkeep@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: inkeep[bot] <257615677+inkeep[bot]@users.noreply.github.com>
Co-authored-by: Dimitri POSTOLOV <dimaMachina@users.noreply.github.com>
* Fix scheduled trigger invocations being skipped when trigger is edited without changing the next execution time

* claude comments
…-frequency deps (#2780)

* Update pullfrog to latest SHA and add daily dependabot group for high-frequency deps

- Update pullfrog from v0.0.178 SHA to v0.0.181 SHA (30d68e5) to stay on
  commit-pinned references for security (action has write permissions + 9 API keys)
- Split dependabot github-actions config into a "high-frequency" group for
  pullfrog with daily schedule, so SHA pins get updated automatically
- This supersedes PR #2757's approach of moving to mutable tag references

https://claude.ai/code/session_01D3ZGYHG8VhsZwqjjXqy2Ap

* Split dependabot github-actions into daily (pullfrog) and monthly (rest)

Separate into two ecosystem entries so pullfrog gets daily SHA updates
while other GitHub Actions stay on a monthly cadence.

https://claude.ai/code/session_01D3ZGYHG8VhsZwqjjXqy2Ap

* Fix invalid dependabot config: merge duplicate github-actions entries

Dependabot disallows duplicate ecosystem+directory pairs. Use a single
entry with two groups instead: high-frequency (pullfrog) and github-actions
(everything else via exclude-patterns).

https://claude.ai/code/session_01D3ZGYHG8VhsZwqjjXqy2Ap

---------

Co-authored-by: Claude <noreply@anthropic.com>
* ci: bootstrap preview auth

* ci: require secure preview auth config

* ci: recover preview auth runtime vars

* ci: install railway in preview bootstrap

* ci: provision preview db tcp proxies

* ci: proxy preview spicedb bootstrap

* ci: harden preview retry and error logging

---------

Co-authored-by: Andrew Mikofalvy <5668128+amikofalvy@users.noreply.github.com>
* Fix misleading scopes placeholder in credential form

The Nango API validates scopes against a strict comma-separated pattern
with no spaces. Updated placeholder and help text to show the correct
format and prevent 400 errors when users enter multiple scopes.

Made-with: Cursor

* Add changeset for scopes placeholder fix

Made-with: Cursor
…dential provider setup (#2776)

* fix(manage-ui): fix URL validation bypass and permission guard in credential provider setup

Reorder Zod schema construction so custom validators (e.g. URL protocol
allowlist) are chained after required/optional base schema instead of
being overwritten. Move all React hooks above the canEdit early-return
guard to satisfy Rules of Hooks, with canEdit checks inside hook bodies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(manage-ui): add server-side URL protocol validation in buildCredentialsPayload

Validate app_link against HTTP/HTTPS allowlist in the server action to
prevent bypassing client-side form validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update agents-manage-ui/src/components/credentials/views/generic-auth-form.tsx

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* fix err

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
* feat(pdf): Support PDF attachments

* Add tests and other review feedback

* Fix doc

* More renaming and cleanup

* refactor: extract Vercel content part schemas to types/chat.ts for reuse

Move inline Zod schemas from chatDataStream.ts and message-parts.ts into
types/chat.ts as shared, exported schemas. This eliminates duplicate
definitions and makes schema management easier.

Co-authored-by: Andrew Mikofalvy <amikofalvy@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Andrew Mikofalvy <amikofalvy@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Composio connected account ID pinning

Pin connected_account_id to Composio MCP URLs to prevent cross-project
credential leakage. Implements "both or none" policy — user_id and
connected_account_id are injected together or not at all.

- Add ComposioCredentialStore for credential lifecycle management
- Update AgentMcpManager and discoverToolsFromServer with pinning logic
- Mark Composio tools without connectedAccountId as needs_auth
- Add generic disconnect credential UI (works for all credential types)
- Store authScheme in credential retrievalParams for display
- Update OAuth login flow to create credential references post-connect
- Add unit tests for new credential store, composio client, and pinning

Made-with: Cursor

* feedback

* fix test
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
#2787)

* Add back link to projects sidebar, add org settings link to user dropdown, adjust sidebar highlight color in dark mode

* Apply suggestion from @claude[bot]

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* Fix bad claude formatting

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…ndpoint (#2782)

* fix: return Vercel AI SDK FileUIPart-compliant file parts from /run conversations endpoint

- Resolve blob:// URIs to proxy HTTP URLs via resolveMessagesListBlobUris()
- Reshape file parts from { data, metadata.mimeType } to { url, mediaType, filename? }
- Matches Vercel AI SDK FileUIPart spec for useChat() compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Skip malformed file parts

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mike Rashkovsky <mike.r@inkeep.com>
…try errors (#2756)

* fix: provide relationshipId for load_skill tool calls in graph events

* fix: treat load_skill as internal tool, suppress chat/graph streaming events
* fix for fetch trace

* fix for fetch trace
…rilled permission flags (`readOnly`, `canEdit`, `canUse`) with direct hook call `useProjectPermissionsQuery()` (#2792)

* upd

* upd

* format

* format

* format

* format

* format

* format

* format

* format

* format

* fix review

* fix breadcrumb on profile page

* Apply suggestions from code review

Co-authored-by: Dimitri POSTOLOV <dmytropostolov@gmail.com>

* Update agents-manage-ui/src/lib/api/projects.ts

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* Update agents-manage-ui/src/app/[tenantId]/profile/layout.tsx

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* style: auto-format with biome

* fix review

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ter connect/disconnect (#2794)

Fetch user-scoped credential server-side in page.tsx (matching the
project-scoped pattern) instead of via a client-side React Query hook.
This ensures router.refresh() after OAuth connect or credential delete
re-fetches the credential data, so the "Your Connection" card updates
without a manual page refresh.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ct` (#2793)

* reuse `useProjectsQuery` instead of `fetchProjectsAction` in `useEffect`

* format

* upd

* fix lint

* Create little-hounds-battle.md
@itoqa
Copy link

itoqa bot commented Mar 20, 2026

Ito Test Report ❌

21 test cases ran. 14 failed, 7 passed.

Across 21 test cases, 14 failed and 7 passed, indicating significant regression risk concentrated in the Skills stack rather than broad instability. The most critical findings were two PR-introduced High-severity code defects—a missing createSkillFile export in the manage UI API client that crashes Skills routes/creation/edit/authz flows, and an unimported SKILL_ENTRY_FILE_PATH constant in backend DAL logic that triggers 422/ReferenceError failures in file create/save paths—plus a separate High-severity encoded traversal validation bypass (%2E%2E persisted), while key guardrails like SKILL.md create/delete protections, duplicate/blank-path rejection, alias routing, and skills unsaved-changes behavior largely worked as expected.

❌ Failed (14)
Category Summary Screenshot
Adversarial ⚠️ Encoded traversal segment %2E%2E bypasses validation and is persisted as a skill file path. ADV-2
Agent 🟠 Agent page marks an empty agent as dirty on initial load, so navigation from a clean page incorrectly triggers the Unsaved changes dialog. AGENT-1
Authz ⚠️ Skills route fails with import/export mismatch before read-only authorization checks can run. AUTHZ-1
Logic ⚠️ Skill creation flow hits compile error due to missing createSkillFile export in API module. LOGIC-1
Mobile ⚠️ Saving a skill file triggers a backend runtime error due to an undefined constant in the DAL update/create/delete paths. MOBILE-1
Rapid ⚠️ Create-file double-submit path hits a backend ReferenceError and cannot complete idempotency validation. RAPID-1
Rapid ⚠️ Skills file route fails with a compile/runtime module export error before save-spam behavior can be exercised. RAPID-2
Happy-path ⚠️ Skills route fails before redirect because createSkillFile import is missing from src/lib/api/skills.ts exports. ROUTE-1
Happy-path ⚠️ Empty-project skills route fails before empty-state render due to the same missing createSkillFile export. ROUTE-2
Happy-path ⚠️ SKILL.md file route cannot load because the same missing export breaks skills route compilation. ROUTE-3
Happy-path ⚠️ Creating an auxiliary skill file fails with 422 due to an undefined constant in the DAL path. ROUTE-4
Happy-path ⚠️ Deep-link setup fails because the prerequisite create-file API call hits the same backend defect. ROUTE-5
Happy-path ⚠️ Skills route crashes before non-entry delete flow can start due to missing createSkillFile export. ROUTE-6
Happy-path ⚠️ SKILL.md file route crashes for the same missing createSkillFile export, blocking delete-skill flow. ROUTE-7
⚠️ Encoded traversal path bypasses skill file validation
  • What failed: The API accepts and stores an encoded traversal segment in filePath; expected behavior is rejection of traversal-equivalent segments before persistence.
  • Impact: Attackers or misconfigured clients can bypass traversal checks with encoded segments and store unsafe path-like values. This weakens file-path invariants and can propagate invalid paths to downstream tooling.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Send a create-file request for an existing skill with filePath set to %2E%2E/escape.txt.
    2. Observe the request succeeds instead of returning a validation error.
    3. Reload the skill file tree and confirm the encoded traversal-like path is present.
  • Code analysis: I reviewed the file-path schema, route validation wiring, and DAL insert flow. Validation rejects literal . and .. segments only, but does not normalize or decode URL-encoded segments before enforcing that rule, and the accepted value is inserted directly.
  • Why this is likely a bug: The production validation logic does not canonicalize encoded path segments, so traversal-equivalent input can pass checks and be committed to persistent storage.

Relevant code:

packages/agents-core/src/validation/schemas/skills.ts (lines 37-48)

const SkillFilePathSchema = z.string().trim().nonempty().max(1024).refine((value) => !value.startsWith('/')).refine((value) => !value.includes('\\')).refine((value) => value.split('/').every((segment) => segment !== '' && segment !== '.' && segment !== '..'));

agents-api/src/domains/manage/routes/skills.ts (lines 81-95)

app.openapi(createProtectedRoute({ method: 'post', path: '/{id}/files', request: { params: TenantProjectIdParamsSchema, body: { content: { 'application/json': { schema: SkillFileApiInsertSchema } } } } }));

packages/agents-core/src/data-access/manage/skills.ts (lines 221-233)

const [createdFile] = await db.insert(skillFiles).values({ tenantId: params.scopes.tenantId, projectId: params.scopes.projectId, skillId: params.skillId, id: generateId(), filePath: params.data.filePath, content: params.data.content, createdAt: now, updatedAt: now });
🟠 Agent page unsaved dialog triggers on clean initial load
  • What failed: The app treats a clean initial load as dirty and blocks navigation with the unsaved-changes dialog, but expected behavior is no prompt until a real user edit occurs.
  • Impact: Users get false data-loss warnings and interrupted navigation on clean loads of empty agents. This degrades trust in the editor state and increases friction for normal navigation.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open /{tenantId}/projects/{projectId}/agents/{agentId} for an editable agent with no existing nodes.
    2. Do not make any user edits.
    3. Click a navigation link away from the agent page (for example, Skills).
    4. Observe that the unsaved-changes dialog appears even though no user change was made.
  • Code analysis: In the agent page client component, initialization auto-adds a node for empty agents and calls markUnsaved() during that automatic setup path. The unsaved-changes dialog intercepts navigation whenever dirty is true, so this auto-initialization path creates a false positive even before user edits.
  • Why this is likely a bug: Production code sets dirty=true from auto-initialization (not user action), and that directly triggers the unsaved-changes interceptor, so the false prompt is caused by application logic rather than test setup.

Relevant code:

agents-manage-ui/src/app/[tenantId]/projects/[projectId]/agents/[agentId]/page.client.tsx (lines 150-157)

const onAddInitialNode = () => {
  const newNode = {
    ...initialNode,
    selected: true,
  };
  clearSelection();
  markUnsaved();
  commandManager.execute(new AddNodeCommand(newNode));

agents-manage-ui/src/app/[tenantId]/projects/[projectId]/agents/[agentId]/page.client.tsx (lines 188-192)

// After initialization, if there are no nodes and copilot is not configured, auto-add initial node
// Only auto-add if user has edit permission
if (agentNodes.length === 0 && (!isCopilotConfigured || !SHOW_CHAT_TO_CREATE) && canEdit) {
  onAddInitialNode();
}

agents-manage-ui/src/components/agent/unsaved-changes-dialog.tsx (lines 57-64)

useEffect(() => {
  if (!dirty) {
    return;
  }
  const requestNavigationConfirmation = (navigate: PendingNavigation) => {
    pendingNavigationRef.current = navigate;
    setShowUnsavedDialog(true);
  };
⚠️ Read-only permission blocks all mutations
  • What failed: The Skills flow fails before authz assertions because createSkillFile is imported by the action layer but not exported by the API module, producing a compile/runtime module error and route failure.
  • Impact: Users cannot reliably access the Skills file-routed workflow when this module path is loaded. This blocks authorization validation and core skill file operations behind the broken route.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Sign in to the local Manage UI and open an existing project.
    2. Navigate to the Skills route (/{tenantId}/projects/{projectId}/skills).
    3. Trigger the file-routed Skills flow so server actions in src/lib/actions/skill-files.ts are loaded.
    4. Observe the route fails when the action module imports createSkillFile from @/lib/api/skills but that export does not exist.
  • Code analysis: I inspected the new skill file server actions and the API client module used by those actions. The action file imports createSkillFile, but the API module defines fetchSkills, fetchSkill, createSkill, updateSkill, updateSkillFile, deleteSkill, and deleteSkillFile only; no createSkillFile export exists, which is a direct code-level defect.
  • Why this is likely a bug: The importing module references a symbol that the target module does not export, so the failure is explained by concrete production code mismatch rather than test setup behavior.

Relevant code:

agents-manage-ui/src/lib/actions/skill-files.ts (lines 3-6)

import { parseSkillFromMarkdown, SkillFrontmatterSchema } from '@inkeep/agents-core';
import { revalidatePath } from 'next/cache';
import { createSkillFile, deleteSkillFile, updateSkillFile } from '@/lib/api/skills';
import { buildSkillFileViewHref, SKILL_ENTRY_FILE_PATH } from '@/lib/utils/skill-files';

agents-manage-ui/src/lib/api/skills.ts (lines 49-67)

export async function createSkill(
  tenantId: string,
  projectId: string,
  skill: SkillApiInsert
): Promise<SkillDetail> {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  const response = await makeManagementApiRequest<SingleResponse<SkillDetail>>(
    `tenants/${tenantId}/projects/${projectId}/skills`,
    {
      method: 'POST',
      body: JSON.stringify(skill),
    }
  );

agents-manage-ui/src/lib/api/skills.ts (lines 90-110)

export async function updateSkillFile(
  tenantId: string,
  projectId: string,
  skillId: string,
  fileId: string,
  file: SkillFileUpdate
): Promise<SkillFile> {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  const response = await makeManagementApiRequest<SingleResponse<SkillFile>>(
    `tenants/${tenantId}/projects/${projectId}/skills/${skillId}/files/${fileId}`,
⚠️ Create skill from form generates entry file
  • What failed: The app fails with a server compile/runtime module error instead of loading the created skill and SKILL.md entry.
  • Impact: Users cannot complete the core skill creation journey in the new file-routed UI. This blocks onboarding and any downstream file editing workflows.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open the New Skill page at /{tenantId}/projects/{projectId}/skills/new.
    2. Enter a valid skill name, content, and metadata, then click Save.
    3. Allow navigation back to the skills file route.
    4. Observe route/module failure instead of the created SKILL.md view.
  • Code analysis: The save path in the file editor invokes createSkillFileAction, which imports createSkillFile from the skills API module. That API module does not export createSkillFile, so route/module evaluation fails before normal rendering.
  • Why this is likely a bug: A statically imported symbol is referenced from a module that does not export it, which deterministically causes route/module failure independent of test setup.

Relevant code:

agents-manage-ui/src/lib/actions/skill-files.ts (lines 3-6)

import { parseSkillFromMarkdown, SkillFrontmatterSchema } from '@inkeep/agents-core';
import { revalidatePath } from 'next/cache';
import { createSkillFile, deleteSkillFile, updateSkillFile } from '@/lib/api/skills';
import { buildSkillFileViewHref, SKILL_ENTRY_FILE_PATH } from '@/lib/utils/skill-files';

agents-manage-ui/src/components/skills/skill-file-editor.tsx (lines 72-75)

const result = isCreateMode
  ? await createSkillFileAction(tenantId, projectId, skillId, nextFilePath, nextContent)
  : await updateSkillFileAction(tenantId, projectId, skillId, fileId, filePath, nextContent);
setIsSaving(false);

agents-manage-ui/src/lib/api/skills.ts (lines 49-58)

export async function createSkill(
  tenantId: string,
  projectId: string,
  skill: SkillApiInsert
): Promise<SkillDetail> {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  const response = await makeManagementApiRequest<SingleResponse<SkillDetail>>(
    `tenants/${tenantId}/projects/${projectId}/skills`,
⚠️ Saving skill files fails due to undefined SKILL_ENTRY_FILE_PATH reference
  • What failed: Save fails with backend error instead of persisting the edit because the DAL references SKILL_ENTRY_FILE_PATH without importing it.
  • Impact: Users cannot reliably save skill file edits, blocking core content management flows. This can affect both mobile and non-mobile paths that execute the same DAL file operations.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Set viewport to iPhone 13 (390x844) and open the skills route as an admin user.
    2. Open a nested skill file from the tree.
    3. Edit file content and click Save.
    4. Observe save fails instead of persisting changes.
  • Code analysis: I traced the save path from the API route to updateSkillFileById in the skills DAL and found SKILL_ENTRY_FILE_PATH used in multiple branches without any import in that module, which yields a runtime ReferenceError when those branches execute.
  • Why this is likely a bug: The module references SKILL_ENTRY_FILE_PATH in executable logic but never imports it, so runtime evaluation can throw and break save flows independent of test setup.

Relevant code:

packages/agents-core/src/data-access/manage/skills.ts (lines 1-20)

import { generateId } from '@inkeep/agents-core';
import { and, asc, count, desc, eq, inArray } from 'drizzle-orm';
import type { AgentsManageDatabaseClient } from '../../db/manage/manage-client';
import { skillFiles, skills, subAgentSkills } from '../../db/manage/manage-schema';
import type {
  SkillFileSelect,
  SkillInsert,
  SkillSelect,
  SubAgentSkillInsert,
  SubAgentSkillWithIndex,
} from '../../types/entities';
import type {
  AgentScopeConfig,
  PaginationConfig,
  ProjectScopeConfig,
  SubAgentScopeConfig,
} from '../../types/utility';
import { getLogger } from '../../utils/logger';
import type { SkillFileInput } from '../../utils/skill-files';

packages/agents-core/src/data-access/manage/skills.ts (lines 209-215)

if (!skill) {
    return null;
  }

  if (params.data.filePath === SKILL_ENTRY_FILE_PATH) {
    throw new Error(`Use the skill update flow to manage ${SKILL_ENTRY_FILE_PATH}`);
  }

packages/agents-core/src/data-access/manage/skills.ts (lines 290-299)

const data =
    existingFile.filePath === SKILL_ENTRY_FILE_PATH
      ? buildEntryFileUpdateData({
          skillId: params.skillId,
          files,
          content: params.content,
        })
      : { files };
⚠️ Rapid double-submit on Create file is idempotent
  • What failed: The create operation throws SKILL_ENTRY_FILE_PATH is not defined instead of completing a single successful create, so idempotency behavior cannot be validated.
  • Impact: Creating non-entry skill files can fail at runtime, blocking core editing workflows in the Skills UI. This prevents users from adding files and can break related create/update/delete operations that share the same code path.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open a project skills page and select an existing skill.
    2. Use the tree/context menu action to add a new non-entry file.
    3. Enter a valid file path and content, then submit create rapidly (double-submit).
    4. Observe the create request fail with a runtime ReferenceError instead of creating one file.
  • Code analysis: I inspected the new skill file CRUD DAL in packages/agents-core/src/data-access/manage/skills.ts and found runtime usage of SKILL_ENTRY_FILE_PATH without any value import from the skill-files utility module.
  • Why this is likely a bug: A runtime identifier is referenced in production code without a corresponding value import, which directly explains the observed ReferenceError and is not attributable to test setup.

Relevant code:

packages/agents-core/src/data-access/manage/skills.ts (lines 12-20)

import type {
  AgentScopeConfig,
  PaginationConfig,
  ProjectScopeConfig,
  SubAgentScopeConfig,
} from '../../types/utility';
import { getLogger } from '../../utils/logger';
import type { SkillFileInput } from '../../utils/skill-files';
import { agentScopedWhere, projectScopedWhere, subAgentScopedWhere } from './scope-helpers';

packages/agents-core/src/data-access/manage/skills.ts (lines 213-215)

if (params.data.filePath === SKILL_ENTRY_FILE_PATH) {
  throw new Error(`Use the skill update flow to manage ${SKILL_ENTRY_FILE_PATH}`);
}

packages/agents-core/src/data-access/manage/skills.ts (lines 339-341)

if (existingFile.filePath === SKILL_ENTRY_FILE_PATH) {
  throw new Error('Use the skill delete flow to remove SKILL.md');
}
⚠️ Rapid save spam does not corrupt content
  • What failed: The route errors before save-spam verification because the file actions module imports createSkillFile from the skills API module, but that export is missing, causing a compile/runtime module failure.
  • Impact: Users cannot reliably create or edit nested skill files from the new file-routed UI flow. Core file management scenarios fail before validation logic or persistence can run.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Navigate to the skills file route for an existing skill.
    2. Start add-file flow or open an editable non-entry file.
    3. Trigger create/save via the skill file server action.
    4. Observe module failure because createSkillFile is imported by actions but missing from the skills API module exports.
  • Code analysis: I compared the skill file server action and skills API client modules: the action expects createSkillFile, but agents-manage-ui/src/lib/api/skills.ts defines update/delete file functions and does not export createSkillFile, creating a hard module mismatch.
  • Why this is likely a bug: The production module graph contains a direct missing export/import contract violation, so the failure is deterministic from code structure alone and not dependent on test harness behavior.

Relevant code:

agents-manage-ui/src/lib/actions/skill-files.ts (lines 3-6)

import { parseSkillFromMarkdown, SkillFrontmatterSchema } from '@inkeep/agents-core';
import { revalidatePath } from 'next/cache';
import { createSkillFile, deleteSkillFile, updateSkillFile } from '@/lib/api/skills';
import { buildSkillFileViewHref, SKILL_ENTRY_FILE_PATH } from '@/lib/utils/skill-files';

agents-manage-ui/src/lib/actions/skill-files.ts (lines 48-51)

const file = await createSkillFile(tenantId, projectId, skillId, {
  filePath: normalizedFilePath,
  content,
});

agents-manage-ui/src/lib/api/skills.ts (lines 90-102)

export async function updateSkillFile(
  tenantId: string,
  projectId: string,
  skillId: string,
  fileId: string,
  file: SkillFileUpdate
): Promise<SkillFile> {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  const response = await makeManagementApiRequest<SingleResponse<SkillFile>>(
    `tenants/${tenantId}/projects/${projectId}/skills/${skillId}/files/${fileId}`,
⚠️ Skills page crashes due to missing createSkillFile export
  • What failed: The page cannot complete the intended redirect to the first skill file because module loading fails first; expected behavior is redirect to /skills/files/... for the first file.
  • Impact: Users cannot access the skills landing workflow in affected builds, blocking core skills management navigation. This breaks the primary entry point for skill-file browsing/editing.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open a project skills landing page with existing skills.
    2. Allow the route to load the file-routed skills UI.
    3. Observe module loading for skill file actions used by the editor path.
    4. The route fails before redirecting to /skills/files/... due to unresolved createSkillFile export.
  • Code analysis: I inspected the skills route and shared file-editor action chain. The new action module imports createSkillFile from src/lib/api/skills.ts, and the editor references that action; however, the API module exports updateSkillFile/deleteSkillFile but never exports createSkillFile, creating a direct unresolved import in production code.
  • Why this is likely a bug: The import/export mismatch is a deterministic code defect in the shipped route/action dependency graph and directly explains the observed route failure without relying on test setup assumptions.

Relevant code:

agents-manage-ui/src/lib/actions/skill-files.ts (lines 3-6)

import { parseSkillFromMarkdown, SkillFrontmatterSchema } from '@inkeep/agents-core';
import { revalidatePath } from 'next/cache';
import { createSkillFile, deleteSkillFile, updateSkillFile } from '@/lib/api/skills';
import { buildSkillFileViewHref, SKILL_ENTRY_FILE_PATH } from '@/lib/utils/skill-files';

agents-manage-ui/src/lib/api/skills.ts (lines 90-102)

export async function updateSkillFile(
  tenantId: string,
  projectId: string,
  skillId: string,
  fileId: string,
  file: SkillFileUpdate
): Promise<SkillFile> {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  const response = await makeManagementApiRequest<SingleResponse<SkillFile>>(
    `tenants/${tenantId}/projects/${projectId}/skills/${skillId}/files/${fileId}`,

agents-manage-ui/src/components/skills/skill-file-editor.tsx (lines 15-16)

import { createSkillFileAction, updateSkillFileAction } from '@/lib/actions/skill-files';
import {
⚠️ Empty skills route blocked by unresolved createSkillFile import
  • What failed: The expected empty-state (No skills yet.) does not reliably render because the same unresolved createSkillFile export causes route execution to fail first.
  • Impact: Empty-project onboarding is blocked because users cannot reach the intended empty-state call-to-action flow. Teams cannot create their first skill from the standard landing route.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open a project with zero skills at the skills landing route.
    2. Allow the with-sidebar skills route to resolve page data and initialize editor/action imports.
    3. Observe route failure before the expected No skills yet. empty-state appears.
  • Code analysis: I reviewed the empty-state route ((with-sidebar)/page.tsx) and the shared skills stack used by this route. The route is part of the same module graph that includes skill file actions/editor, and the missing createSkillFile export in src/lib/api/skills.ts creates a compile/runtime failure that prevents route execution for empty projects as well.
  • Why this is likely a bug: The route behavior depends on a module graph containing an unresolved production import, so failure before empty-state rendering is consistent with concrete code-level breakage rather than test-only interference.

Relevant code:

agents-manage-ui/src/app/[tenantId]/projects/[projectId]/skills/(with-sidebar)/page.tsx (lines 24-29)

if (!files.length) {
  return <EmptyState title="No skills yet." description={metadata.description} action={action} />;
}

redirect(buildSkillFileViewHref(tenantId, projectId, files[0].skillId, files[0].filePath));

agents-manage-ui/src/lib/actions/skill-files.ts (lines 48-52)

const file = await createSkillFile(tenantId, projectId, skillId, {
  filePath: normalizedFilePath,
  content,
});

agents-manage-ui/src/lib/api/skills.ts (lines 112-123)

export async function deleteSkill(tenantId: string, projectId: string, skillId: string) {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  await makeManagementApiRequest(`tenants/${tenantId}/projects/${projectId}/skills/${skillId}`, {
    method: 'DELETE',
  });
  revalidatePath(`/${tenantId}/projects/${projectId}/skills`);
}

export async function deleteSkillFile(
⚠️ Edit SKILL.md and persist metadata/content
  • What failed: The SKILL.md editor route fails to render because the same unresolved createSkillFile import breaks skills module compilation.
  • Impact: Users cannot open and edit skill entry files, so metadata/content persistence checks and routine maintenance are blocked. This affects both direct links and normal sidebar navigation into files.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Navigate to /{tenantId}/projects/{projectId}/skills/files/{skillId}/SKILL.md.
    2. Load the page through direct URL or sidebar selection.
    3. Observe failure before the SKILL.md editor can render.
  • Code analysis: The file route renders SkillFileEditor, which imports server actions from skill-files.ts; that action file imports a non-existent createSkillFile export from the API helper. Because this is in the route dependency graph, the route crashes even before user edits.
  • Why this is likely a bug: The broken import/export contract is in production route code and directly explains why the file page crashes without relying on any test-only mutation.

Relevant code:

agents-manage-ui/src/app/[tenantId]/projects/[projectId]/skills/(with-sidebar)/files/[...fileSlug]/page.tsx (lines 23-31)

return (
  <SkillFileEditor
    tenantId={tenantId}
    projectId={projectId}
    skillId={selectedFile.skillId}
    fileId={selectedFile.fileId}
    filePath={selectedFile.filePath}
    initialContent={selectedFile.content}
    canEdit={permissions.canEdit}
  />
);

agents-manage-ui/src/components/skills/skill-file-editor.tsx (lines 15-16)

import { createSkillFileAction, updateSkillFileAction } from '@/lib/actions/skill-files';
import {

agents-manage-ui/src/lib/actions/skill-files.ts (lines 48-51)

const file = await createSkillFile(tenantId, projectId, skillId, {
  filePath: normalizedFilePath,
  content,
});
⚠️ Create nested auxiliary file from tree context menu
  • What failed: The create-file request returns 422 with SKILL_ENTRY_FILE_PATH is not defined instead of creating and selecting the new file.
  • Impact: Users cannot create non-entry files in skill directories, which blocks core nested-file workflows and follow-on routing scenarios. This prevents normal skill-authoring flows from completing.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open the Skills tree for an existing skill.
    2. Choose Add file from the context menu.
    3. Submit a nested path such as templates/day/qa-checklist.txt with content.
    4. Observe that the API returns 422 with SKILL_ENTRY_FILE_PATH is not defined.
  • Code analysis: I inspected the backend DAL and route handler and found runtime references to symbols that are used but not imported in the skill file mutation path; the route converts thrown errors into 422 responses, matching the observed behavior.
  • Why this is likely a bug: The create-file production code path dereferences SKILL_ENTRY_FILE_PATH without importing it, which causes a runtime ReferenceError that the API maps to 422.

Relevant code:

packages/agents-core/src/data-access/manage/skills.ts (lines 1-20)

import { generateId } from '@inkeep/agents-core';
import { and, asc, count, desc, eq, inArray } from 'drizzle-orm';
import type { AgentsManageDatabaseClient } from '../../db/manage/manage-client';
import { skillFiles, skills, subAgentSkills } from '../../db/manage/manage-schema';
import type {
  SkillFileSelect,
  SkillInsert,
  SkillSelect,
  SubAgentSkillInsert,
  SubAgentSkillWithIndex,
} from '../../types/entities';

packages/agents-core/src/data-access/manage/skills.ts (lines 197-215)

export const createSkillFileById =
  (db: AgentsManageDatabaseClient) =>
  async (params: {
    scopes: ProjectScopeConfig;
    skillId: string;
    data: SkillFileApiInsert;
  }): Promise<SkillFileSelect | null> => {
    const skill = await getSkillByIdWithFiles(db)({
      scopes: params.scopes,
      skillId: params.skillId,
    });

    if (params.data.filePath === SKILL_ENTRY_FILE_PATH) {
      throw new Error(`Use the skill update flow to manage ${SKILL_ENTRY_FILE_PATH}`);
    }

agents-api/src/domains/manage/routes/skills.ts (lines 136-140)

if (error instanceof Error) {
  throw createApiError({
    code: error.message.includes('already exists') ? 'conflict' : 'unprocessable_entity',
    message: error.message,
  });
}
⚠️ Deep-link to encoded nested file path
  • What failed: The prerequisite file creation step fails with the same 422 backend error (SKILL_ENTRY_FILE_PATH is not defined), so deep-link validation cannot proceed.
  • Impact: Users cannot establish or verify deep links for newly created nested files because file creation fails first. This breaks a key navigation path for multi-file skills.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Open the file creation flow for an existing skill.
    2. Attempt to create a file with a spaced nested path such as reference/day one/notes.txt.
    3. Use or construct the encoded deep-link URL for that path.
    4. Observe that the flow is blocked because create-file returns 422 with SKILL_ENTRY_FILE_PATH is not defined.
  • Code analysis: I inspected the deep-link routing utilities and the backend create-file path. The UI route encoding logic is present, but this scenario fails earlier in backend file creation due to the same missing-import defect in the DAL.
  • Why this is likely a bug: The failing behavior occurs before deep-link resolution and is explained by a concrete backend runtime defect in production create-file logic.

Relevant code:

agents-manage-ui/src/lib/utils/skill-files.ts (lines 32-38)

export function encodeSkillFileRoutePath(routePath: string): string {
  return routePath
    .split('/')
    .filter(Boolean)
    .map((segment) => encodeURIComponent(segment))
    .join('/');
}

packages/agents-core/src/data-access/manage/skills.ts (lines 213-215)

if (params.data.filePath === SKILL_ENTRY_FILE_PATH) {
  throw new Error(`Use the skill update flow to manage ${SKILL_ENTRY_FILE_PATH}`);
}
⚠️ Skills page crashes from missing createSkillFile export
  • What failed: The page fails to load due to a server import/export mismatch, so the delete confirmation flow never becomes reachable.
  • Impact: Users cannot access the primary skills file editor route, which blocks deletion and related file-management workflows. This prevents critical CRUD operations in the skills UI.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Navigate to /{tenantId}/projects/{projectId}/skills for a project with existing skills.
    2. Open the file editor context for a non-entry file.
    3. Attempt to trigger the non-entry file delete flow.
  • Code analysis: I reviewed the skills UI server action and API client modules. skill-files.ts imports and calls createSkillFile, but api/skills.ts only exports update/delete helpers for files and never exports createSkillFile, causing route/module load failure.
  • Why this is likely a bug: The code path statically imports and calls a symbol that the target module does not export, which is a deterministic application code defect rather than test setup noise.

Relevant code:

agents-manage-ui/src/lib/actions/skill-files.ts (lines 3-6)

import { parseSkillFromMarkdown, SkillFrontmatterSchema } from '@inkeep/agents-core';
import { revalidatePath } from 'next/cache';
import { createSkillFile, deleteSkillFile, updateSkillFile } from '@/lib/api/skills';
import { buildSkillFileViewHref, SKILL_ENTRY_FILE_PATH } from '@/lib/utils/skill-files';

agents-manage-ui/src/lib/actions/skill-files.ts (lines 48-51)

const file = await createSkillFile(tenantId, projectId, skillId, {
      filePath: normalizedFilePath,
      content,
    });

agents-manage-ui/src/lib/api/skills.ts (lines 90-110)

export async function updateSkillFile(
  tenantId: string,
  projectId: string,
  skillId: string,
  fileId: string,
  file: SkillFileUpdate
): Promise<SkillFile> {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  const response = await makeManagementApiRequest<SingleResponse<SkillFile>>(
    `tenants/${tenantId}/projects/${projectId}/skills/${skillId}/files/${fileId}`,
⚠️ SKILL.md route crashes from missing createSkillFile export
  • What failed: The route crashes before the SKILL.md delete-skill confirmation can render because the same missing export breaks module loading.
  • Impact: Users cannot open the SKILL.md file route to perform entry-file management actions, including the required skill deletion flow. Core destructive operations are blocked for affected skills.
  • Introduced by this PR: Yes – this PR modified the relevant code
  • Steps to reproduce:
    1. Navigate directly to /{tenantId}/projects/{projectId}/skills/files/{skillId}/SKILL.md.
    2. Wait for the file editor route to initialize.
    3. Attempt to begin the delete-skill confirmation flow from the SKILL.md editor.
  • Code analysis: I traced the same server-action dependency chain used by the SKILL.md route. The editor imports createSkillFileAction, which depends on createSkillFile from api/skills.ts; that function is not exported, so route initialization fails.
  • Why this is likely a bug: The failing route depends on a server action whose required API helper is missing from exports, so the failure is explained directly by production source code.

Relevant code:

agents-manage-ui/src/components/skills/skill-file-editor.tsx (lines 15-16)

import { createSkillFileAction, updateSkillFileAction } from '@/lib/actions/skill-files';
import {

agents-manage-ui/src/components/skills/skill-file-editor.tsx (lines 72-74)

const result = isCreateMode
      ? await createSkillFileAction(tenantId, projectId, skillId, nextFilePath, nextContent)
      : await updateSkillFileAction(tenantId, projectId, skillId, fileId, filePath, nextContent);

agents-manage-ui/src/lib/api/skills.ts (lines 122-138)

export async function deleteSkillFile(
  tenantId: string,
  projectId: string,
  skillId: string,
  fileId: string
) {
  validateTenantId(tenantId);
  validateProjectId(projectId);

  await makeManagementApiRequest(
    `tenants/${tenantId}/projects/${projectId}/skills/${skillId}/files/${fileId}`,
    {
✅ Passed (7)
Category Summary Screenshot
Adversarial Duplicate file path creation was blocked and the original file content remained unchanged. ADV-1
Adversarial SKILL.md frontmatter name mismatch did not persist after save attempt and reload. ADV-3
Adversarial Using Playwright request context, both abuse operations were blocked (422) after IDs were resolved from skill detail; backend enforcement prevented creating or deleting SKILL.md through file endpoints. ADV-4
Adversarial PATCH request omitting SKILL.md was rejected (500 in this build), and subsequent detail fetch confirmed files were preserved with SKILL.md intact, so no silent data wipe occurred. ADV-5
Edge File ID alias resolution is implemented correctly and matches expected route behavior. EDGE-1
Edge Whitespace-only file path was rejected with a required-path validation error and no file creation. EDGE-2
Journey Unsaved-changes guard behaved correctly in the skills editor: Go back preserved edits, and Discard navigated away without applying unsaved changes. JOURNEY-1

Commit: 310ecb4

View Full Run


Tell us how we did: Give Ito Feedback

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.

9 participants