feat(plugins): Foundations Pack — OS basics (apps, clipboard, files, state) - #25
Merged
Conversation
Cross-platform pure-Node plugin layering OS basics on top of the
desktop driver. Lets agents touch the rest of the machine, not just
the running app they're driving. 14 new tools across four surfaces:
Apps agentmark_app_run
Clipboard agentmark_clipboard_read, …_write
Files agentmark_files_list, …_read, …_write, …_stat,
…_delete, …_move, …_mkdir
State agentmark_state_get, …_set, …_delete, …_list
App launcher uses platform-native invocation (macOS `open`, Windows
`start`, Linux `xdg-open` for files) so file associations + Start
Menu / Launch Services resolution work for free.
Filesystem operations are gated by an allowlist of root directories
(defaults: cwd + os.tmpdir; configurable via fileRoots config option
or AGENTMARK_FILES_ROOTS env var). Every path is realpath'd before
the prefix check so symlinks cannot escape the boundary. Roots are
canonicalised at first call (macOS /var → /private/var alias).
Durable state is a single JSON file at
~/.thinkfleet/agentmark/state.json (0600), updated via atomic
write-temp-and-rename. Suitable for small slow-changing data:
last-customer worked with, retry counters, cached enumerations.
Clipboard shells out to OS utilities (pbcopy/pbpaste, Get-Clipboard /
Set-Clipboard, xclip/wl-paste). No external Node dependency.
Opt-in plugin — not part of the default set. Compose:
import { createMcpServer, createWebPlugin, createPdfPlugin,
createDesktopPlugin, createMetaPlugin,
createFoundationsPlugin } from '@thinkfleet/agentmark'
const foundations = createFoundationsPlugin({
fileRoots: ['/Users/me/Documents'],
})
createMcpServer({ plugins: [web, pdf, desktop, foundations, meta] })
Tests: 16 new (filesystem allowlist + escape resistance + symlink
follow, state round-trip + persistence + prefix filter, plugin
registration). 355 pass / 10 skip total. Build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why
Foundation for everything in the "AI agent does anything on a desktop" mandate. Today the desktop driver can manipulate a running app's accessibility tree, but the agent can't:
These four surfaces unblock all of that. Together they're the OS-basics layer that complements (not replaces) the desktop driver.
Tools shipped
Safety
Filesystem ops are gated by an allowlist (default: `cwd` + `os.tmpdir`). Every path is canonicalised via `realpath` before the prefix check, so symlinks like `~/Documents/safe → /etc` can't escape. Roots are themselves canonicalised on first use to handle macOS's `/var → /private/var` alias.
App launch detaches by default so the spawned app survives the MCP server's death; safe for any UI app.
State store writes atomically (temp file + rename), mode 0600 on POSIX. Single global namespace per user — not a database, don't put megabytes in it.
Usage
```ts
import {
createMcpServer,
createWebPlugin, createPdfPlugin, createDesktopPlugin, createMetaPlugin,
createFoundationsPlugin,
} from '@thinkfleet/agentmark'
const foundations = createFoundationsPlugin({
fileRoots: ['/Users/me/Documents', '/Users/me/Downloads'],
})
createMcpServer({
plugins: [web, pdf, desktop, foundations, meta],
})
```
Test plan
What's next in the roadmap
🤖 Generated with Claude Code