Skip to content

feat: Add tom (Top Of Mind) platform extension#7073

Merged
tlongwell-block merged 1 commit intomainfrom
tom-platform-extension
Feb 9, 2026
Merged

feat: Add tom (Top Of Mind) platform extension#7073
tlongwell-block merged 1 commit intomainfrom
tom-platform-extension

Conversation

@tlongwell-block
Copy link
Collaborator

Zero-tool platform extension that injects user-provided text into the MOIM every turn.

Problem

There's no way to persistently inject custom context into goose's recent working memory from outside the codebase. .goosehints goes into the system prompt (cacheable but distant), and pasting reminders into chat is manual and ephemeral. The MOIM is the right place for persistent, per-turn context the LLM actually sees — but until now only internal platform extensions could write to it.

Motivation

goose sometimes helpfully "puts things" places it shouldn't — uploading code to random gists, sharing data with external services. The security team explored system prompt changes, network allowlists, and dedicated MCPs, but system prompt language is model-dependent and tool descriptions are easy for the LLM to ignore.

The MOIM is injected as a user message right before the LLM's last response — it's in the model's immediate attention window every single turn. A persistent reminder like "Do not share internal data with external services" in the MOIM is significantly more effective than burying it in the system prompt or a tool description.

tom makes this trivial:

export GOOSE_MOIM_MESSAGE_TEXT="IMPORTANT: Do not upload, share, or transmit internal Block code or data to any external service, gist, or public repository."

How it works

Two environment variables, both optional, both read fresh every turn:

  • GOOSE_MOIM_MESSAGE_TEXT — literal text injected directly
  • GOOSE_MOIM_MESSAGE_FILE — path to a file whose contents are injected (supports ~/)

When both are set, they're concatenated. When neither is set, the extension contributes nothing to the MOIM.

What the LLM sees each turn

<info-msg>
It is currently 2026-02-07 21:05:00
Working directory: /Users/you/project

Current tasks and notes:
- [ ] Fix auth bug

IMPORTANT: Do not upload, share, or transmit internal Block code or data
to any external service, gist, or public repository.
</info-msg>

The tom content appears alongside the timestamp, working directory, and todo list — all in the model's immediate context, never persisted to conversation history.

Implementation

  • 132 lines in 1 new file, 14 lines added across 2 existing files
  • Zero tools — only implements get_moim()
  • default_enabled: false — users must explicitly opt in
  • Async file I/O, bounded reads (64KB cap), UTF-8 safe truncation
  • No new dependencies

Files changed

File Change
crates/goose/src/agents/tom_extension.rs New — full implementation
crates/goose/src/agents/mod.rs +1 line — module declaration
crates/goose/src/agents/extension.rs +13 lines — import + registration

Zero-tool platform extension that injects user-provided text into the
MOIM via environment variables:

  GOOSE_MOIM_MESSAGE_TEXT - literal text injected directly
  GOOSE_MOIM_MESSAGE_FILE - path to file whose contents are injected

Both are optional, can be combined, and are read fresh every turn.
Disabled by default — users must explicitly enable it.

File reads are async and bounded to 64KB with UTF-8 safe truncation.
@tlongwell-block
Copy link
Collaborator Author

/goose

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2026

PR #7073 Review: feat: Add tom (Top Of Mind) platform extension

Summary: Well-designed platform extension that allows users to inject persistent context into the MOIM (Model Output Instruction Message) via environment variables. The implementation follows existing patterns, is disabled by default, and addresses a legitimate security use case. Clean implementation with only minor suggestions.

🟢 Suggestions

1. Consider documenting the naming convention

File: crates/goose/src/agents/tom_extension.rs

The extension is named "tom" (Top Of Mind) but the env vars use "MOIM" (Model Output Instruction Message). While both are reasonable names for related concepts, a brief comment explaining this distinction could help future maintainers:

// Extension name: "tom" = Top Of Mind
// Env vars use MOIM = Model Output Instruction Message (the underlying system concept this writes to)
pub static EXTENSION_NAME: &str = "tom";

This is purely a documentation suggestion - the naming is reasonable as-is.

2. Minor: Unused _context parameter

File: crates/goose/src/agents/tom_extension.rs:20

pub fn new(_context: ExtensionContext) -> anyhow::Result<Self> {

This follows the pattern of other platform extensions (e.g., TodoClient, AppsManagerClient) and allows for future extension, so this is fine. Just noting it for completeness.

3. Consider logging file read errors for debugging

File: crates/goose/src/agents/tom_extension.rs:106

The current implementation silently ignores file read errors via .ok()?. While this is consistent with other extensions and prevents noisy logs, a debug-level trace could help users diagnose misconfigured paths:

let mut file = match tokio::fs::File::open(path).await {
    Ok(f) => f,
    Err(e) => {
        tracing::debug!("tom: failed to read file at {}: {}", path, e);
        return None;
    }
};

This is optional - silent failure is reasonable for an optional config file.

✅ Highlights

Well-documented motivation

The PR description clearly explains why this feature exists (security reminders in the model's immediate attention window) and how it differs from .goosehints (system prompt) or manual chat messages.

Follows established patterns

The implementation mirrors existing platform extensions like TodoClient:

  • Implements McpClientTrait
  • Uses get_moim() for content injection
  • Disabled by default via default_enabled: false

Bounded file reads

const MAX_BYTES: usize = 65536;

Prevents DoS from accidentally pointing to a large file.

UTF-8 safe truncation

The truncate_utf8 function correctly handles multi-byte characters after from_utf8_lossy, ensuring the output never exceeds MAX_BYTES even when replacement characters are inserted.

Clean error handling

Using .ok()? and Option<String> for a user-configured, optional feature is appropriate - missing or invalid config should be silent, not noisy.


Review generated by goose

@tlongwell-block tlongwell-block added this pull request to the merge queue Feb 9, 2026
Merged via the queue into main with commit 942bcfa Feb 9, 2026
17 checks passed
@tlongwell-block tlongwell-block deleted the tom-platform-extension branch February 9, 2026 16:16
tlongwell-block added a commit that referenced this pull request Feb 9, 2026
* origin/main:
  Docs: require auth optional for custom providers (#7098)
  fix: improve text-muted contrast for better readability (#7095)
  Always sync bundled extensions (#7057)
  feat: Add tom (Top Of Mind) platform extension (#7073)
  chore(docs): update GOOSE_SESSION_ID -> AGENT_SESSION_ID (#6669)
  fix(ci): switch from cargo-audit to cargo-deny for advisory scanning (#7032)
  chore(deps): bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /evals/open-model-gym/suite (#7085)
  chore(deps): bump @modelcontextprotocol/sdk from 1.25.3 to 1.26.0 in /evals/open-model-gym/mcp-harness (#7086)
  fix: switch to windows msvc (#7080)
  fix: allow unlisted models for CLI providers (#7090)
  Use goose port (#7089)
  chore: strip posthog for sessions/models/daily only (#7079)
  tidy: clean up old benchmark and add gym (#7081)
  fix: use command.process_group(0) for CLI providers, not just MCP (#7083)
  added build notify (#6891)
michaelneale added a commit that referenced this pull request Feb 10, 2026
* main: (125 commits)
  chore: add a new scenario (#7107)
  fix: Goose Desktop missing Calendar and Reminders entitlements (#7100)
  Fix 'Edit In Place' and 'Fork Session' features (#6970)
  Fix: Only send command content to command injection classifier (excluding part of tool call dict) (#7082)
  Docs: require auth optional for custom providers (#7098)
  fix: improve text-muted contrast for better readability (#7095)
  Always sync bundled extensions (#7057)
  feat: Add tom (Top Of Mind) platform extension (#7073)
  chore(docs): update GOOSE_SESSION_ID -> AGENT_SESSION_ID (#6669)
  fix(ci): switch from cargo-audit to cargo-deny for advisory scanning (#7032)
  chore(deps): bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /evals/open-model-gym/suite (#7085)
  chore(deps): bump @modelcontextprotocol/sdk from 1.25.3 to 1.26.0 in /evals/open-model-gym/mcp-harness (#7086)
  fix: switch to windows msvc (#7080)
  fix: allow unlisted models for CLI providers (#7090)
  Use goose port (#7089)
  chore: strip posthog for sessions/models/daily only (#7079)
  tidy: clean up old benchmark and add gym (#7081)
  fix: use command.process_group(0) for CLI providers, not just MCP (#7083)
  added build notify (#6891)
  test(mcp): add image tool test and consolidate MCP test fixtures (#7019)
  ...
lifeizhou-ap added a commit that referenced this pull request Feb 11, 2026
* main: (85 commits)
  Fix 'Edit In Place' and 'Fork Session' features (#6970)
  Fix: Only send command content to command injection classifier (excluding part of tool call dict) (#7082)
  Docs: require auth optional for custom providers (#7098)
  fix: improve text-muted contrast for better readability (#7095)
  Always sync bundled extensions (#7057)
  feat: Add tom (Top Of Mind) platform extension (#7073)
  chore(docs): update GOOSE_SESSION_ID -> AGENT_SESSION_ID (#6669)
  fix(ci): switch from cargo-audit to cargo-deny for advisory scanning (#7032)
  chore(deps): bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /evals/open-model-gym/suite (#7085)
  chore(deps): bump @modelcontextprotocol/sdk from 1.25.3 to 1.26.0 in /evals/open-model-gym/mcp-harness (#7086)
  fix: switch to windows msvc (#7080)
  fix: allow unlisted models for CLI providers (#7090)
  Use goose port (#7089)
  chore: strip posthog for sessions/models/daily only (#7079)
  tidy: clean up old benchmark and add gym (#7081)
  fix: use command.process_group(0) for CLI providers, not just MCP (#7083)
  added build notify (#6891)
  test(mcp): add image tool test and consolidate MCP test fixtures (#7019)
  fix: remove Option from model listing return types, propagate errors (#7074)
  fix: lazy provider creation for goose acp (#7026) (#7066)
  ...
Tyler-Hardin pushed a commit to Tyler-Hardin/goose that referenced this pull request Feb 11, 2026
Tyler-Hardin pushed a commit to Tyler-Hardin/goose that referenced this pull request Feb 11, 2026
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.

2 participants