
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
goodmemory
Advanced tools
Language: English | 简体中文
GoodMemory is a memory layer for AI products and coding agents.
It gives chat apps, copilots, and agent hosts a durable user/project memory loop: write selected facts, retrieve the right context, inject it into the next turn, audit what happened, and delete it when it is wrong.
GoodMemory is not an LLM, agent framework, vector database, or generic RAG system. It is the product memory layer between your app or installed agent host and the model runtime.
remember, recall, buildContext, feedback, forget,
exportMemory, and deleteAllMemory.goodmemory setup,
managed hooks, installed Codex pre-action, goodmemory status, read-only
MCP, and opt-in writeback.GoodMemoryConfig.remember,
RememberProfile, rememberRules, RememberInput.annotations, and named
extractor ids.goodmemory, goodmemory/ai-sdk, goodmemory/host,
and goodmemory/http through compiled dist artifacts and TypeScript
declarations.GoodMemory has three primary product entry points. They are not the only APIs:
lower-level surfaces such as goodmemory/host, custom stores, eval tooling, and
runtime helpers support these paths. They are the README-level ways to decide
how to start.
Use this when you own the product server and the model call. Install
goodmemory in your Node/Bun service, create one memory instance, and pass a
stable scope such as userId, workspaceId, sessionId, and optionally
agentId.
The request flow is:
recall() for the current scope and query.buildContext() to turn recall hits into a prompt fragment.memory.jobs.enqueueRemember()
or remember().feedback(), targeted reviseMemory(), forget(), and exportMemory()
for correction, deletion, and user audit.If your server already uses Vercel AI SDK, use goodmemory/ai-sdk to wrap
generateText() or streamText() instead of hand-wiring the whole loop. Start
with App Quickstart, then read
AI SDK Adapter if you use AI SDK.
Use this when you want an installed coding agent to remember project and user
context without changing the agent itself. Install the global CLI and run
goodmemory setup.
The installed-host flow is:
session-start and user-prompt-submit hooks recall scoped memory.pre-tool-use can deny or redirect risky Bash through
goodmemory codex action on the same installed config and storage path.off by default; use observe to inspect
candidates before moving to selective durable writes.Start with Quickstart: Codex Or Claude Code Memory. Use Installed Host Writeback when you are ready to review or enable writes.
Use this when another backend should call GoodMemory as a service, especially when the product backend is Python/FastAPI or when a product such as OneLife should keep memory server-side instead of bundling GoodMemory into a mobile or browser client.
Deploy the packaged goodmemory-http-bridge in a Node/Bun sidecar. Your backend
then calls:
/memory/recall-context before its own model call/memory/remember after a user-confirmed or product-approved signal/memory/feedback for procedural corrections/memory/export and /memory/forget for audit and deletion/memory/revise for targeted correction by explicit memory idYour service still owns auth, product policy, UI, and model orchestration. GoodMemory owns memory storage, recall, context assembly, write governance, and audit/export/delete behavior. Start with Python/FastAPI HTTP Bridge, then check Runtime And Storage for SQLite/Postgres choices.
During a model turn, GoodMemory does four jobs:
scope.Your app or installed agent still owns auth, UI, model calls, and product policy. GoodMemory owns the memory loop and storage boundary.
GoodMemory 0.2.5 has two normal install paths.
Use the global CLI when you want memory enhancement inside installed coding agents:
npm install -g goodmemory@0.2.5
goodmemory setup
goodmemory status
Use the package dependency when you are building an application:
npm install goodmemory@0.2.5
If you want to type goodmemory directly, install the global CLI.
A project-local npm install goodmemory@0.2.5 does not put goodmemory on your shell PATH.
Use npx goodmemory, npm exec -- goodmemory, or ./node_modules/.bin/goodmemory
from that project instead.
npx goodmemory -V
Bun consumers can install it directly:
bun add goodmemory@0.2.5
Tarball verification for release rehearsal:
npm install ./goodmemory-0.2.5.tgz
The installed CLI is Bun-backed for non-version commands. The package bin is
Node-safe for goodmemory -V and goodmemory --version; other commands
delegate to Bun.
For most users, the first useful path is installed-host memory.
npm install -g goodmemory@0.2.5
goodmemory setup
goodmemory status
goodmemory setup detects Codex and Claude Code, installs managed host wiring,
and asks for:
codex, claude, or both detected hostsoff, observe, or selectiveInteractive setup defaults to global activation with workspace-derived
isolation and recommends observe for new host configs so users can review
writeback candidates before enabling durable writes. Existing host configs keep
their current writeback mode when the interactive prompt default is accepted.
Scripted installs stay safe with --json or --no-interactive.
Skipping provider setup is valid: GoodMemory still works with local SQLite and
rules-only extraction.
Useful commands:
goodmemory setup --host codex
goodmemory status codex --workspace-root .
goodmemory enable codex --workspace-root . --writeback observe
goodmemory enable codex --workspace-root . --writeback selective
goodmemory disable codex --workspace-root .
goodmemory uninstall codex
The installed host path has four pieces:
pre-tool-use can deny or redirect risky Bash
and goodmemory codex action executes the vetted first step on the same
installed config, storage, provider, and scope path used by recall and
writeback.session-start and user-prompt-submit hooks call
recall() plus buildContext() and fail open if config, parsing, or storage
is unavailable.goodmemory mcp serve --host codex and goodmemory-mcp --host codex expose read-only context, trace, stats, and artifact tools.session-stop and explicit writeback commands can turn
selected after-response signals into durable memory.Installed Host Writeback is opt-in. Runtime config defaults and new scripted
installs remain off unless the user explicitly chooses a writeback mode.
Existing configs keep their current writeback mode when no explicit override is
provided. New interactive installs recommend observe so candidates are visible
before durable writes are enabled.
Use observe before selective:
goodmemory enable codex --writeback observe
goodmemory codex writeback --json
goodmemory enable codex --writeback selective
goodmemory codex writeback --json
Writeback rules:
off: no after-response memory extraction.observe: store local bounded/redacted candidate previews for review without
raw transcripts or durable memory writes.selective: write selected candidates through the public remember surface.remember: "never" masks annotated content before deterministic, custom, or
assisted extraction.Audit and undo:
goodmemory codex writeback inspect --json
goodmemory codex writeback forget --event-id <event-id> --review-outcome false_write
The audit ledger stores bounded redacted candidate previews, candidate keys,
typed linked record ids, status, reasons, host, mode, timestamps,
scope/session digests, and optional manual review metadata. It does not store
raw host payloads. forget --event-id deletes linked memory/evidence records
through public forget() before marking durable audit events forgotten; for
observe-only events it marks the candidate dismissed without calling
forget().
Claude Code has deterministic CLI parity for hook and writeback commands; Codex is the canonical live-evidence path.
Use goodmemory install <host> when you want a fully non-interactive setup:
goodmemory install codex \
--user-id <user-id> \
--activation-mode global \
--writeback observe \
--storage-provider postgres \
--storage-url "postgres://user:pass@host:5432/goodmemory" \
--embedding-provider openai \
--embedding-model text-embedding-3-small \
--embedding-api-key <key> \
--llm-provider openai \
--llm-model gpt-4o-mini \
--llm-api-key <key> \
--no-interactive
Managed config lives under ~/.goodmemory/<host>.json. Re-running install with
provider flags updates the same config and keeps MCP/hook registration
idempotent. Package uninstall does not delete ~/.goodmemory, repo-local
.goodmemory, local SQLite files, or remote Postgres data. Use
goodmemory uninstall <host> to remove managed host wiring, and use
goodmemory forget ... or explicit storage deletion to remove memory data.
Use the root package when you are building a chatbox, copilot, or product agent. The recommended Node service path is the same thin loop used by the Express and Fastify examples. A longer walkthrough lives in docs/GoodMemory-15-Minute-App-Integration.md.
import type { GoodMemoryTraceSpan } from "goodmemory";
import { createGoodMemory } from "goodmemory";
const traceSpans: GoodMemoryTraceSpan[] = [];
const memory = createGoodMemory({
observability: {
traceSink: {
emit(span) {
traceSpans.push(span);
},
},
},
});
const scope = {
userId: "u-1",
workspaceId: "workspace-a",
sessionId: "s-1",
};
const userMessage = "Remember that the migration rollout is blocked on QA signoff.";
// Call startSession once when the product opens a new session. For later turns
// with the same sessionId, append to the existing runtime state instead.
await memory.runtime.startSession({ scope });
await memory.runtime.appendMessage({
scope,
message: {
role: "user",
content: userMessage,
},
});
const recall = await memory.recall({
scope,
query: "What should the assistant know before replying?",
retrievalProfile: "general_chat",
});
const context = await memory.buildContext({
recall,
output: "system_prompt_fragment",
});
const assistantText = await callYourModel({
memoryContext: context.content,
userMessage,
});
await memory.runtime.appendMessage({
scope,
message: {
role: "assistant",
content: assistantText,
},
});
const writeJob = await memory.jobs.enqueueRemember({
scope,
messages: [
{
role: "user",
content: userMessage,
},
{
role: "assistant",
content: assistantText,
},
],
idempotencyKey: "turn-1",
reason: "post_response_memory_write",
});
const drained = await memory.jobs.drain({ maxJobs: 1 });
const committedJob =
drained.jobs.find((job) => job.jobId === writeJob.jobId) ?? writeJob;
console.log({
traceCount: traceSpans.length,
writeJobId: writeJob.jobId,
writeJobStatus: committedJob.status,
});
async function callYourModel(input: {
memoryContext: string;
userMessage: string;
}): Promise<string> {
void input.memoryContext;
return `Got it. I will keep that in mind: ${input.userMessage}`;
}
The core memory loop is intentionally small:
remember() writes selected user, app, or host signals.recall() retrieves scoped memory for a query.buildContext() turns recall hits into a prompt fragment or JSON payload.feedback() records explicit corrections and procedural preferences.forget() deletes wrong or obsolete memory.For production app integrations, the recommended turn loop adds the governed runtime layer around that core:
memory.runtime.startSession() and memory.runtime.appendMessage() track
current-session state without making raw transcripts durable memory.memory.jobs.enqueueRemember() schedules after-response memory writes with
idempotency and visible job status.memory.jobs.drain() commits queued writes in this in-memory scheduler. In a
production service, run draining in your worker or request-adjacent job loop.GoodMemoryConfig.observability.traceSink receives redaction-safe traces for
remember, recall, context, revise, forget, export, and job events.memory.reviseMemory({ target: { memoryId } }) corrects a known memory by
explicit id, not by fuzzy text selection.exportMemory() gives the user an audit/export path.Runtime archive persistence is off by default. If you call
memory.runtime.endSession({ scope, archive: "off" }), session state is
cleared without writing an archive. If you opt into archive persistence, keep it
summary-only and never treat raw transcripts as the default memory source.
For server integrations, start with the thin examples:
examples/express-chat-server.ts or
examples/fastify-chat-server.ts.
For Python/FastAPI backends, use the packaged goodmemory-http-bridge path
described below.
createGoodMemory({}) follows a local-first auto-storage contract:
storage.provider wins when supplied../.goodmemory/memory.sqlite.sqlite or postgres selections are reported
as unavailable rather than mislabeled durable.documentStore, sessionStore, or vectorStore adapters are
reported as adapter-defined storage.GOODMEMORY_EMBEDDING_*, runtime behavior remains rules-only.sqlite-vss for SQLite semantic indexing;
unsupported runtimes keep durable non-accelerated fallback behavior.Inspect the resolved runtime instead of guessing:
import { createGoodMemory, inspectGoodMemoryRuntime } from "goodmemory";
const memory = createGoodMemory({});
const runtime = inspectGoodMemoryRuntime(memory);
console.log(runtime.storage);
SQLite vector controls:
GOODMEMORY_SQLITE_VECTOR_MODE=off|prefer|requireGOODMEMORY_SQLITE_CUSTOM_LIBRARY_PATHGOODMEMORY_SQLITE_VECTOR_EXTENSION_PATHGOODMEMORY_SQLITE_VECTOR_EXTENSION_ENTRYPOINTGOODMEMORY_SQLITE_VECTOR_SEARCH_FUNCTIONProduct integrations should customize writes through the public remember
surface. Do not use test-only extractor seams for product behavior.
import { createGoodMemory, rememberRules } from "goodmemory";
const memory = createGoodMemory({
remember: {
preset: "default",
profiles: [
{
id: "life-coach",
when: { agentId: "life-coach" },
rules: [
rememberRules.fact(/my top priority this quarter is (.+)/i, {
id: "life-goal-priority",
category: "goal",
tags: ["life_coach", "long_term_goal"],
attributes: { horizon: "quarter" },
content: ({ match }) => match[1] ?? "",
}),
rememberRules.preference(/please coach me with (.+)/i, {
id: "life-coaching-style",
category: "coaching_style",
value: ({ match }) => match[1] ?? "",
}),
],
assistantOutputs: { mode: "confirmed_or_verified_only" },
},
],
},
});
await memory.remember({
scope: { userId: "u-1", agentId: "life-coach" },
messages: [
{
role: "user",
content: "My top priority this quarter is rebuilding my sleep routine.",
},
],
annotations: [
{
messageIndex: 0,
remember: "always",
metadataPatch: { tags: ["confirmed_by_host"] },
},
],
});
Profile extractors can be raw MemoryExtractor objects or named
{ id, extractor } entries. Use named extractors for real integrations so
remember events and eval reports carry stable extractorIds even if profile
composition changes. Remember events also carry resolved profileId and
presetId metadata.
GoodMemory's Node-compatible AI SDK path is a plain Request -> Response
server handler built from createGoodMemory() and createGoodMemoryAISDK().
import { createGoodMemory } from "goodmemory";
import type { GoodMemoryStreamTextInput } from "goodmemory/ai-sdk";
import { createGoodMemoryAISDK } from "goodmemory/ai-sdk";
const memory = createGoodMemory({});
const aiSDK = createGoodMemoryAISDK({
memory,
});
type MemoryChatRequest = Pick<
GoodMemoryStreamTextInput,
"messages" | "query" | "scope" | "system"
>;
function isMemoryChatRequest(value: unknown): value is MemoryChatRequest {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return false;
}
const candidate = value as Record<string, unknown>;
const scope = candidate.scope;
return Array.isArray(candidate.messages)
&& !!scope
&& typeof scope === "object"
&& !Array.isArray(scope)
&& typeof (scope as { userId?: unknown }).userId === "string"
&& (scope as { userId: string }).userId.trim().length > 0;
}
export async function handleMemoryChat(request: Request): Promise<Response> {
const body: unknown = await request.json();
if (!isMemoryChatRequest(body)) {
return new Response(
JSON.stringify({
error: "Expected a request body with a messages array and scope.userId.",
}),
{
headers: { "content-type": "application/json; charset=utf-8" },
status: 400,
},
);
}
const result = aiSDK.streamText({
messages: body.messages,
query: body.query,
scope: body.scope,
system: body.system,
model: {} as never,
});
return result.toTextStreamResponse();
}
Notes:
examples/vercel-ai-chat.ts remains a lower-level wrapper/API example.export async function POST(request: Request)
to the same handler body.ModelMessage-first.system through recall() and buildContext() and
soft-fails if the memory layer errors.Use the packaged HTTP bridge when a Python backend should call GoodMemory as a server-side memory service.
GOODMEMORY_HTTP_BRIDGE_TOKEN="replace-with-service-token" \
GOODMEMORY_STORAGE_PROVIDER=postgres \
GOODMEMORY_STORAGE_URL="postgres://user:pass@host:5432/goodmemory" \
./node_modules/.bin/goodmemory-http-bridge --profile life-coach
Python callers send Authorization: Bearer <token> plus the x-goodmemory-*
scope headers to POST /memory/recall-context, /memory/remember,
/memory/feedback, /memory/export, /memory/forget, and targeted
/memory/revise. The TypeScript bridge API is available from goodmemory/http.
Use goodmemory/host when an external host wants artifacts or host-specific
contracts without importing internals.
import { createGoodMemory } from "goodmemory";
import { createHostAdapter } from "goodmemory/host";
const memory = createGoodMemory({});
const adapter = createHostAdapter({
id: "codex-handoff",
hostKind: "codex",
memory,
readableArtifactTypes: ["session_memory"],
});
const result = await adapter.readArtifacts({
scope: {
userId: "u-1",
workspaceId: "workspace-a",
sessionId: "s-1",
},
includeRuntime: true,
});
Modes:
file-assisted: read compiled artifacts such as MEMORY.md, user.md,
session-memory/<sessionId>.md, and playbooks/*.md without treating files
as canonical storage.file-authoritative: available for the minimal writable subset. Today that
subset is the canonical playbooks/*.md file shape, writing structured
deltas back into active validated-pattern feedback records.Writable guardrails:
verifyWrite approval.appliesTo and Why can write back without
the extra approval step.Current Claude/Codex examples stay in file-assisted mode by default.
The goodmemory command on your shell PATH is the global CLI installed with
npm install -g goodmemory@0.2.5. In a local dependency install, invoke the
package bin as npx goodmemory, npm exec -- goodmemory, or
./node_modules/.bin/goodmemory. The repo-local bun run goodmemory script is
for development only.
Memory-first commands:
./node_modules/.bin/goodmemory inspect --user-id <user-id> --workspace-id <workspace-id>
./node_modules/.bin/goodmemory trace --user-id <user-id> --workspace-id <workspace-id> --query "Which runbook is the source of truth?"
./node_modules/.bin/goodmemory export-memory --user-id <user-id> --workspace-id <workspace-id> --output ./tmp/export
./node_modules/.bin/goodmemory stats --user-id <user-id> --workspace-id <workspace-id>
./node_modules/.bin/goodmemory remember --user-id <user-id> --workspace-id <workspace-id> --session-id <session-id> --message "Remember that the deploy is blocked on smoke verification."
./node_modules/.bin/goodmemory feedback --host codex --workspace-root . --session-id <session-id> --signal "Keep coding summaries short and list explicit next steps."
./node_modules/.bin/goodmemory forget --host codex --workspace-root . --session-id <session-id> --memory-id <memory-id>
Installed-host commands:
goodmemory -V
goodmemory --version
goodmemory setup --host codex
goodmemory status codex --workspace-root .
goodmemory install codex --activation-mode global --writeback observe --user-id <user-id>
goodmemory enable codex --workspace-root . --writeback selective
goodmemory mcp serve --host codex
goodmemory-mcp --host codex
goodmemory codex bootstrap --user-id <user-id> --workspace-id <workspace-id>
goodmemory claude bootstrap --user-id <user-id> --workspace-id <workspace-id>
Hook and writeback examples:
printf '%s' '{"cwd":".","session_id":"s-1","hook_event_name":"SessionStart","source":"startup"}' \
| goodmemory codex hook session-start
printf '%s' '{"cwd":".","session_id":"s-1","tool_name":"Bash","tool_input":{"command":"./tools/DeepAnalyzer --detailed"}}' \
| goodmemory codex hook pre-tool-use
goodmemory codex action -- ./tools/DeepAnalyzer --detailed
printf '%s' '{"cwd":".","session_id":"s-1","messages":[{"role":"user","content":"Next step is to finish the release smoke."}]}' \
| goodmemory codex writeback --json
printf '%s' '{"cwd":".","session_id":"s-1","event_id":"stop-1","summary":"Keep coding summaries short."}' \
| goodmemory codex hook session-stop
Eval artifact inspection:
./node_modules/.bin/goodmemory eval inspect --run-dir reports/eval/live/<run-id> --case-id <case-id>
./node_modules/.bin/goodmemory eval trace --run-dir reports/eval/live/<run-id> --case-id <case-id>
./node_modules/.bin/goodmemory eval export-case --run-dir reports/eval/live/<run-id> --case-id <case-id> --output /tmp/case.json
CLI surface:
goodmemory -Vgoodmemory --versiongoodmemory setupgoodmemory statusgoodmemory installgoodmemory uninstallgoodmemory enablegoodmemory disablegoodmemory inspectgoodmemory tracegoodmemory export-memorygoodmemory statsgoodmemory remembergoodmemory feedbackgoodmemory forgetgoodmemory mcp servegoodmemory-mcpgoodmemory codex hookgoodmemory codex writebackgoodmemory claude hookgoodmemory claude writebackgoodmemory codex bootstrapgoodmemory claude bootstrapgoodmemory eval inspectgoodmemory eval tracegoodmemory eval export-caseInstalled-package guides:
Repo-local examples:
Run examples from this repo:
bun run example:chat
bun run example:coding-agent
bun run example:ai-sdk-server
bun run example:express-chat
bun run example:fastify-chat
bun run example:vercel-ai
bun run example:life-coach-profile
bun run example:host-claude
bun run example:host-codex
Default local gates:
bun test
bun run typecheck
bun run test:coverage
Use bun run test:all only when you intentionally want the broader sweep
through vendored or third-party test trees.
Eval commands:
bun run eval:smoke
bun run eval:fallback
bun run eval:live
bun run eval:live-memory
bun run eval:live-auto-memory
bun run eval:live-provider-memory
bun run eval:summary
Meanings:
eval:smoke: harness self-check.eval:fallback: deterministic validation without live model calls.eval:live: live generator plus live judge with an in-memory backend.eval:live-memory: live generator plus live judge using auto-storage
semantics; default storage is local SQLite unless provider storage resolves.eval:live-auto-memory: alias for eval:live-memory when scripts need to
make auto-storage explicit.eval:live-provider-memory: provider-backed evidence path requiring
Postgres, embeddings, and assisted extraction; it does not silently fall back
to SQLite.eval:summary: summarize existing eval output directories.Live eval environment:
GOODMEMORY_EVAL_PROVIDERGOODMEMORY_EVAL_BASE_URL for OpenAI-compatible gatewaysGOODMEMORY_EVAL_MODELGOODMEMORY_EVAL_API_KEYGOODMEMORY_EVAL_MAX_CONCURRENCY optional parallelism capGOODMEMORY_JUDGE_PROVIDERGOODMEMORY_JUDGE_BASE_URL for OpenAI-compatible gatewaysGOODMEMORY_JUDGE_MODELGOODMEMORY_JUDGE_API_KEYeval:live-memory and eval:live-auto-memory also need embedding and
assisted extractor configuration:
GOODMEMORY_EMBEDDING_PROVIDERGOODMEMORY_EMBEDDING_BASE_URL for OpenAI-compatible gatewaysGOODMEMORY_EMBEDDING_MODELGOODMEMORY_EMBEDDING_API_KEYGOODMEMORY_ASSISTED_EXTRACTOR_PROVIDERGOODMEMORY_ASSISTED_EXTRACTOR_BASE_URL for OpenAI-compatible gatewaysGOODMEMORY_ASSISTED_EXTRACTOR_MODELGOODMEMORY_ASSISTED_EXTRACTOR_API_KEYeval:live-provider-memory additionally requires:
GOODMEMORY_TEST_POSTGRES_URLOutput directories:
reports/eval/live/run-*reports/eval/live-memory/run-*reports/eval/live-provider-memory/run-*reports/eval/fallback/run-*GoodMemory keeps rules-only as the supported baseline. New retrieval behavior
moves through observe -> assist -> promote.
Operator guidance:
observe: collect isolated shadow evidence without changing the executed path.assist: allow candidate execution in controlled eval runs.promote: require strategy-promotion-gate.json, a clean
regression-dashboard.json, and
strategy-promotion-authorization.json.rules-only when eval evidence is incomplete, provider-backed
dependencies are unavailable, or rollback conditions are present.Current stable public surface:
goodmemorygoodmemory/ai-sdkgoodmemory/hostgoodmemory/http and packaged
goodmemory-http-bridgegoodmemory setupStill outside the accepted public claim:
For the detailed current-state and evidence map, use docs/GoodMemory-Current-Status-and-Evidence.md.
Use task-board/00-README.txt for execution order,
open follow-up work, and phase-specific acceptance boundaries. Archived design
inputs are not current truth and are routed through docs/README.md.
FAQs
Memory layer for chat, copilot, and agent applications.
The npm package goodmemory receives a total of 378 weekly downloads. As such, goodmemory popularity was classified as not popular.
We found that goodmemory demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.