AI Agent Dependency Lockfiles: Stop Silent Tool and Package Drift A new practitioner guide argues that standard package-lock.json files are necessary but incomplete for AI agents, because agents also depend on models, prompts, MCP tool servers, policy bundles and retrieval configurations that can change behavior without any application code change. The guide recommends recording the full resolved dependency graph — selectors alongside immutable identities such as model revision IDs, image and tool-schema digests, package integrity hashes and index revisions — in a dedicated AI agent dependency lockfile, citing current public guidance on agent supply chains that places models, agents, libraries, developer tools and MCP-server packages in scope. It stresses that the lockfile should hold only references and hashes, never credentials or raw customer data, and that a single command should resolve inputs, validate them and write a canonical lockfile. AI Agent Dependency Lockfiles: Stop Silent Tool and Package Drift An agent can behave differently on Tuesday even when nobody changed its prompt or application code. A package resolves to a newer release, an MCP server updates a tool description, a model alias moves, or a tool image is rebuilt. The workflow still starts. The tests may still be green. But the agent is no longer the runtime you reviewed. That is why a normal package-lock.json is necessary but incomplete for an AI feature. It pins part of the application, while the agent also depends on models, prompts, tool servers, policy bundles, retrieval configurations, and external packages. Treat those as one dependency graph. Then record the resolved graph in an AI agent dependency lockfile. This is not a claim that a lockfile makes an agent safe. It gives you a specific, reviewable answer to a much more useful question: what exactly did this agent run with? That answer makes bad changes easier to spot, reproduce, and roll back. Start with dependencies that can change behavior or permissions without changing the business workflow. A useful lockfile records both the requested selector and the resolved identity. Dependency Weak reference Useful locked identity Model fast-model provider, model revision or immutable deployment ID Prompt support-v3 repository commit and content digest MCP tool billing-tools:latest image/package digest, server version, tool-schema digest Library agent-sdk:^4 exact package version and integrity hash Policy default-policy policy bundle digest and decision version Retrieval config help-center index revision, embedding model, and chunking config digest The point is not to freeze every choice forever. The point is to separate intent from resolution. latest can remain an input to an update job, but it should not be the only record of what production executed. Teams often review source changes carefully and accept an environment variable such as MODEL=fast-model or TOOLS URL=https://tools.internal as harmless wiring. In an agent system, that wiring can change output quality, tool availability, permissions, latency, or data handling. The same issue appears with agent-installed dependencies. If a coding agent adds a package because it recognizes a familiar name, the pull request can hide a new transitive tree behind one import. If an agent fetches a tool server by a mutable tag, an image rebuild can change the runtime after review. Current public guidance on agent supply chains explicitly includes models, agents, libraries, developer tools, and MCP-server packages in scope. The boundary is wider than requirements.txt. Use a format your tools can parse and your reviewers can read. JSON is convenient for CI, though YAML or TOML works too. This example deliberately avoids storing credentials and raw customer data. { "lockVersion": 1, "agent": { "name": "refund-assistant", "workflowCommit": "8e9b1f2", "policy": { "selector": "refund-policy", "resolvedDigest": "sha256:6b4d..." } }, "model": { "selector": "support-fast", "provider": "example-provider", "resolvedId": "support-fast-2026-10-18" }, "prompts": { "path": "prompts/refund.md", "gitCommit": "8e9b1f2", "sha256": "f18c..." } , "tools": { "name": "get order", "serverImage": "registry.example/tools@sha256:a91d...", "serverVersion": "2.4.1", "schemaSha256": "ce33...", "scopes": "orders.read" } , "packages": { "agent-sdk": { "version": "4.2.3", "integrity": "sha512-..." } }, "retrieval": { "indexRevision": "kb-2026-10-23-04", "embeddingModel": "embed-small-r17", "chunkerDigest": "sha256:9d70..." } } Two details matter here. First, the selector preserves the human-facing intent, while the immutable ID or digest preserves what actually resolved. Second, the lockfile captures only references and hashes. A lockfile should never become a second secret store. Create one command that resolves inputs, validates them, and writes a canonical lockfile. A minimal TypeScript-style sketch looks like this: type ResolvedTool = { name: string; serverImage: string; schemaSha256: string; scopes: string ; }; async function lockTool name: string : Promise