Oski turns Slack messages, cron jobs, and CLI tasks into tool-using agent runs, backed by three explicit memory layers: factual, behavioral, procedural. When it hits a capability gap, it can scaffold a new tool. It cannot grant that tool trust. That decision stays with your team.
Two extremes. One middle path.
Most internal agent projects pick an extreme: a static integration list, or an unconstrained agent with a code execution tool. Oski is built for the ground between them, where an agent can genuinely grow inside boundaries a small team can actually audit.
A fixed set of integrations someone wires up once. Safe, but it never grows past what a human anticipated on day one.
An agent with a code execution tool and no boundary. It can do anything, including the one thing nobody approved.
An agent that can propose new capability and grow its own tool surface, inside boundaries your team sets. It can suggest. It cannot self-authorize.
One process. No hidden moving parts.
Everything below is a single Node.js process: an in-memory queue, a concurrency-1 runner, a typed tool registry. No message broker, no worker fleet, no vector store. That is a scope decision for a small-team-sized agent, not an oversight.
Three memory layers. Nothing implicit.
Most agent demos have exactly one kind of memory: whatever fits in the prompt. Oski names three, separately, each with its own update path and its own blast radius.
Approved workspace files, scoped to OSKI_WORKSPACE_ROOTS. Read live, per task. Never cached or embedded, so it can't go stale between reads.
instructions.md, loaded fresh into the system prompt every single turn. Anyone on the team corrects it live with oski learn: in Slack. No redeploy, ever.
The typed tool registry. Builtins ship as a PR. Generated tools are scaffolded on demand, land in src/tools/generated/, and stay untrusted until a human reviews them.
None of these are vector stores or embeddings. Factual memory is a direct file read. Behavioral memory is a plain-text file versioned in git. Procedural memory is a directory of TypeScript files. That's a deliberate simplicity choice for a small-team-scale agent, not a placeholder for something more exotic.
One task, one thread, one decision.
A simulation of the implemented Slack Socket Mode thread flow. A human approval posts the saved draft and writes the approval log.
This is the entire boundary between the model and your filesystem.
Not a paragraph promising safety. The actual check, straight from src/tools/builtin/read_file.ts. Symlinks are resolved and re-checked so a link inside an approved root can't point outside it.
// Both the lexically resolved path and the symlink-resolved real path
// must fall inside an allowed root, so symlinks cannot escape the sandbox.
function isInsideRoot(target: string, root: string): boolean {
const rel = path.relative(root, target);
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
}
function isAllowed(target: string): boolean {
const roots = getWorkspaceRoots();
return roots.some(root => isInsideRoot(target, root));
}
// ...
const real = fs.realpathSync(resolved);
if (!isAllowed(real)) {
return { error: `Access denied: ${requested} resolves outside the configured workspace roots.` };
}
= no access. There is deliberately no default workspace root.
A typed ToolDefinition with a declared read / draft / live scope. Drop it in, restart, done.
Model, tokens, estimated USD, and every tool name called, one JSONL line per turn.
No autonomy theater.
Every claim on this page maps to real, readable code. Here is the honest split, straight from the repo.
Grow capability. Not trust.
The agent can propose new capability. It cannot grant itself trust. Five controls are enforced in code. Review of generated tools is deliberately procedural.
Most "AI agent" repos are a chat wrapper around three tool calls and a good README. I wanted to answer a narrower question: how much real operating discipline can you put around a model before it's actually defensible to hand a small team's internal ops to it?
Oski is that answer, built to be read line by line, not taken on faith: a typed tool registry, a queue, a hard budget, draft-first side effects, three named memory layers, and a narrow, human-gated path for the agent to grow its own tools.
I'm the founder of ChiefOS, the AI operating layer for founders and executive teams. ChiefOS applies these same patterns to real company context, executive workflows, and private integrations. Oski is the public framework behind it, open-sourced so other builders don't have to solve the agent safety and execution layer from scratch.
I'm not selling autonomy. I'm selling the boundary around it.
Inspect it. Constrain it. Extend it.
Small teams do not need another chatbot. They need an internal agent that can operate across real context and grow its own tool surface, without giving up human control. Oski is MIT licensed and built to be read, not just run.