
Research
/Security News
PolinRider Spreads Through Compromised GitHub Accounts and Packagist
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.
@office-agents/sdk
Advanced tools
Headless SDK for Office Agents — agent runtime, tools, storage, VFS, skills, OAuth, and provider config for building AI-powered Office Add-ins
Headless SDK for building AI-powered Microsoft Office Add-ins. Provides an agent runtime, tool system, virtual filesystem, session storage, and multi-provider LLM integration — all running in the browser.
Browser-only — this package targets browser environments (Office Add-ins, SPAs). It uses IndexedDB, localStorage, and the DOM.
npm install @office-agents/sdk
The SDK is organized into several modules:
| Module | Description |
|---|---|
| AgentContext | Owns the VFS, bash shell, static files, skill files, and custom commands for one agent instance |
| Runtime | AgentRuntime — manages agent lifecycle, streaming, model resolution, sessions |
| Tools | defineTool, createBashTool, createReadTool — define and register tools for the agent |
| VFS | In-memory virtual filesystem with bash shell (just-bash) — file uploads, custom commands |
| Storage | IndexedDB-backed session persistence, VFS file storage, skill file storage |
| Provider Config | Multi-provider LLM configuration (OpenAI, Anthropic, Google, etc.) |
| OAuth | PKCE OAuth flow helpers for provider authentication |
| Skills | Installable skill system (prompt snippets + files mounted at /home/skills/) |
| Web | Web search and fetch with pluggable providers |
| Sandbox | SES-based sandboxed JavaScript evaluation |
import { defineTool, toolSuccess } from "@office-agents/sdk";
import { Type } from "@sinclair/typebox";
const greetTool = defineTool({
name: "greet",
label: "Greet",
description: "Greet someone by name",
parameters: Type.Object({
name: Type.String({ description: "Name to greet" }),
}),
execute: async (toolCallId, params) => {
return toolSuccess({ message: `Hello, ${params.name}!` });
},
});
The RuntimeAdapter interface connects your app to the agent runtime:
import type { RuntimeAdapter } from "@office-agents/sdk";
const adapter: RuntimeAdapter = {
tools: [greetTool],
buildSystemPrompt: (skills, commandSnippets) => {
let prompt = "You are a helpful assistant. Use tools when appropriate.";
if (commandSnippets.length > 0) {
prompt += "\n\nAvailable commands:\n" + commandSnippets.join("\n");
}
return prompt;
},
getDocumentId: async () => {
return "my-document-id"; // unique ID for session scoping
},
// Optional: provide static files to mount in the VFS (e.g., API docs)
staticFiles: {
"/home/user/docs/api-reference.d.ts": "declare const MyApi: any;",
},
// Optional: register custom bash commands
customCommands: (ns) => ({
commands: [/* CustomCommand instances */],
promptSnippets: ["Use `my-cmd <arg>` to do something"],
}),
// Optional: inject context into each message
getDocumentMetadata: async () => ({
metadata: { title: "My Document", sheets: ["Sheet1"] },
}),
// Optional: react to tool results (e.g., navigate to a cell)
onToolResult: (toolCallId, result, isError) => {
console.log("Tool result:", result);
},
// Optional: scope storage to your app
storageNamespace: {
dbName: "MyAppDB",
dbVersion: 1,
localStoragePrefix: "my-app",
documentSettingsPrefix: "my-app",
},
};
AgentContext owns all per-instance state: the virtual filesystem, bash shell, static files, skill files, and custom commands. The runtime applies adapter config to the context during init().
import { AgentContext, AgentRuntime } from "@office-agents/sdk";
// Option A: let runtime.init() apply adapter.staticFiles / adapter.customCommands
const ctx = new AgentContext({
namespace: adapter.storageNamespace,
});
// Option B: pass everything upfront (app entrypoints typically do this)
const ctx = new AgentContext({
namespace: adapter.storageNamespace,
staticFiles: adapter.staticFiles,
customCommands: adapter.customCommands,
});
const runtime = new AgentRuntime(adapter, ctx);
// Subscribe to state changes
runtime.subscribe((state) => {
console.log("Messages:", state.messages.length);
console.log("Streaming:", state.isStreaming);
});
// Initialize (applies adapter config, loads saved provider config, restores session + VFS)
await runtime.init();
// Send a message
await runtime.sendMessage("Hello, who are you?");
The VFS and bash shell are accessed through AgentContext:
import { AgentContext } from "@office-agents/sdk";
const ctx = new AgentContext({
staticFiles: {
"/home/user/docs/guide.txt": "Getting started...",
},
});
// Write files to VFS
await ctx.writeFile("/home/user/uploads/data.csv", "name,age\nAlice,30");
// Relative paths resolve to /home/user/uploads/
await ctx.writeFile("notes.txt", "some notes");
// Read files
const content = await ctx.readFile("/home/user/uploads/data.csv");
// List uploads
const uploads = await ctx.listUploads(); // ["data.csv", "notes.txt"]
// Execute bash commands
const bash = ctx.bash;
const result = await bash.exec("ls /home/user/uploads/");
// Snapshot and restore VFS state (used for session persistence)
const snapshot = await ctx.snapshotVfs(); // excludes /home/skills/
await ctx.restoreVfs(snapshot);
// Update skill files in-place (preserves user uploads)
await ctx.setSkillFiles({
"/home/skills/analysis/SKILL.md": new TextEncoder().encode("# Analysis skill"),
});
// Update static files in-place (preserves user uploads)
await ctx.setStaticFiles({
"/home/user/docs/new-guide.txt": "Updated guide",
});
The SDK provides factory functions that create tools bound to an AgentContext:
import { createBashTool, createReadTool } from "@office-agents/sdk";
const ctx = new AgentContext();
// Tools that operate on the context's VFS
const bashTool = createBashTool(ctx);
const readTool = createReadTool(ctx);
// Or use tools as a function of context in your adapter:
const adapter: RuntimeAdapter = {
tools: (ctx) => [createBashTool(ctx), createReadTool(ctx), greetTool],
// ...
};
All config functions take a StorageNamespace to scope localStorage keys:
import { loadSavedConfig, saveConfig, type StorageNamespace } from "@office-agents/sdk";
const ns: StorageNamespace = {
dbName: "MyAppDB",
dbVersion: 1,
localStoragePrefix: "my-app",
documentSettingsPrefix: "my-app",
};
// Load saved config from localStorage
const config = loadSavedConfig(ns);
// Save a new config
saveConfig(ns, {
provider: "openai",
apiKey: "sk-...",
model: "gpt-4o",
useProxy: false,
proxyUrl: "",
thinking: "none",
followMode: true,
expandToolCalls: false,
});
Session and VFS persistence functions are scoped by namespace and document ID:
import {
createSession,
listSessions,
getSession,
saveSession,
deleteSession,
loadVfsFiles,
saveVfsFiles,
type StorageNamespace,
} from "@office-agents/sdk";
const ns: StorageNamespace = {
dbName: "MyAppDB",
dbVersion: 1,
localStoragePrefix: "my-app",
documentSettingsPrefix: "my-app",
};
const docId = "my-document-id";
// Create and manage sessions
const session = await createSession(ns, docId, "My Chat");
const sessions = await listSessions(ns, docId);
await deleteSession(ns, session.id);
// Persist VFS files per session
const snapshot = await ctx.snapshotVfs();
await saveVfsFiles(ns, session.id, snapshot);
const restored = await loadVfsFiles(ns, session.id);
await ctx.restoreVfs(restored);
Skills are installable packages containing a SKILL.md with frontmatter and optional supporting files. They're mounted at /home/skills/<name>/ in the VFS:
import {
addSkill,
getInstalledSkills,
removeSkill,
syncSkillsToVfs,
buildSkillsPromptSection,
AgentContext,
type StorageNamespace,
} from "@office-agents/sdk";
const ns: StorageNamespace = { /* ... */ };
const ctx = new AgentContext({ namespace: ns });
// Install a skill from files
await addSkill(ns, ctx, [
{
path: "SKILL.md",
data: "---\nname: data-analysis\ndescription: Data analysis best practices\n---\n\n# Data Analysis\n\nWhen analyzing data, always start by summarizing the dataset...",
},
]);
// List installed skills
const skills = await getInstalledSkills(ns);
// Build the skills section for the system prompt
const promptSection = buildSkillsPromptSection(skills);
// Sync all skill files into the VFS (preserves uploads)
await syncSkillsToVfs(ns, ctx);
// Uninstall
await removeSkill(ns, ctx, "data-analysis");
Register custom commands that appear in the bash shell and generate prompt snippets:
import { getSharedCustomCommands, type CustomCommandsResult, type StorageNamespace } from "@office-agents/sdk";
function getCustomCommands(ns: StorageNamespace): CustomCommandsResult {
// getSharedCustomCommands provides built-in commands like
// pdf-to-text, docx-to-text, xlsx-to-csv, web-search, web-fetch
return getSharedCustomCommands({ ns });
}
const ctx = new AgentContext({
customCommands: getCustomCommands,
});
// Command snippets are derived from registered commands
const snippets = ctx.commandSnippets; // string[]
new AgentContext(opts?) — Create a context with optional namespace, staticFiles, skillFiles, customCommands..vfs — The InMemoryFs instance (lazy-initialized)..bash — The Bash shell instance (lazy-initialized)..commandSnippets — Prompt snippets derived from registered custom commands..writeFile(path, content) / .readFile(path) / .readFileBuffer(path) / .fileExists(path) / .deleteFile(path) — File operations (relative paths resolve to /home/user/uploads/)..listUploads() — List files in /home/user/uploads/ (excludes .keep)..snapshotVfs() — Snapshot all files except /home/skills/..restoreVfs(files) — Reset and restore from snapshot + static/skill overlays..setStaticFiles(files) / .setSkillFiles(files) — Update mounted files in-place (preserves user data)..setCustomCommands(factory) — Replace custom command factory (rebuilds bash)..reset() — Discard VFS and bash (next access re-initializes from static + skill files).new AgentRuntime(adapter, context) — Create a runtime with an adapter and context..init() — Apply adapter config, load saved provider config, restore session and VFS..sendMessage(text, images?) — Send a user message and stream the response..applyConfig(config) — Apply a provider configuration..newSession() / .switchSession(id) / .deleteCurrentSession() — Session management..uploadFiles(files) / .removeUpload(name) — Manage VFS uploads..installSkill(files) / .uninstallSkill(name) — Install/remove skills..subscribe(listener) — Subscribe to RuntimeState changes..dispose() — Abort streaming and clean up.tools — AgentTool[] or (ctx: AgentContext) => AgentTool[].buildSystemPrompt(skills, commandSnippets) — Build the system prompt.getDocumentId() — Return a unique document ID for session scoping.staticFiles? — Files to mount in VFS (applied during init()).customCommands? — Custom command factory (applied during init()).getDocumentMetadata? — Inject context metadata into each prompt.onToolResult? — React to tool results.metadataTag? — XML tag name for metadata injection.storageNamespace? — Override default storage namespace.defineTool(config) — Create a typed tool with name, description, parameters (TypeBox schema), and execute function.toolSuccess(data) / toolError(message) / toolText(text) — Build tool results.createBashTool(ctx) — Create a bash tool bound to a context.createReadTool(ctx) — Create a file-read tool bound to a context.createSession(ns, workbookId, name?) / saveSession(ns, session) / deleteSession(ns, id) — Session CRUD.listSessions(ns, workbookId) / getSession(ns, id) — Query sessions.getOrCreateCurrentSession(ns, workbookId) — Get or create the current session for a document.saveVfsFiles(ns, sessionId, files) / loadVfsFiles(ns, sessionId) — Persist VFS files per session.getOrCreateDocumentId(ns) — Get or create a persistent document ID.loadSavedConfig(ns) / saveConfig(ns, config) — Read/write provider settings from localStorage.buildCustomModel(config) — Build a model instance from a custom base URL config.applyProxyToModel(model, config) — Apply proxy URL to a model.addSkill(ns, ctx, files) — Install a skill from file inputs.removeSkill(ns, ctx, name) — Uninstall a skill.getInstalledSkills(ns) — List installed skills with metadata.syncSkillsToVfs(ns, ctx) — Sync all skill files into the VFS.buildSkillsPromptSection(skills) — Build the prompt section listing available skills.parseSkillMeta(content) — Parse SKILL.md frontmatter.generatePKCE() — Generate PKCE code verifier + challenge.buildAuthorizationUrl(provider, ...) / exchangeOAuthCode(...) / refreshOAuthToken(...) — Full OAuth flow.searchWeb(query, options) / searchImages(query, options) — Web search with pluggable providers.fetchWeb(url, options) — Fetch and extract web page content.MIT
FAQs
Headless SDK for Office Agents — agent runtime, tools, storage, VFS, skills, OAuth, and provider config for building AI-powered Office Add-ins
The npm package @office-agents/sdk receives a total of 9 weekly downloads. As such, @office-agents/sdk popularity was classified as not popular.
We found that @office-agents/sdk 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.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.

Company News
Allow myself to introduce... myself.