Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .corp-harness/site.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"schema": "corporate-site-site/v1",
"site_id": "corpos",
"corporate_program": null,
"corporate_handoff_sha256": null,
"corporate_program": "corpos-autonomous-company",
"corporate_handoff_sha256": "c0bb90e70fdfe705192e435869c30ab401339ec6b47c0576be34d7f48bae1975",
"verify_argv": ["./scripts/harness/verify.sh"],
"adversarial_argv": ["./scripts/harness/adversarial.sh"]
}
30 changes: 14 additions & 16 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@
# NEVER commit a real `.env` (it is gitignored). Keep secrets out of code,
# logs, and agent output.

# ── Model providers ──────────────────────────────────────────────────
# Provide the key(s) for the provider(s) you actually use.
# OpenRouter is the default for agent reasoning (model: Owl Alpha).
# ── Runtime mode ─────────────────────────────────────────────────────
# local (default): approvals/kill do not require a shared token.
# shared: require DASHBOARD_API_TOKEN for approval and kill mutations.
# CORPOS_MODE=local
# DASHBOARD_API_TOKEN=

# ── Data ─────────────────────────────────────────────────────────────
# Local libsql / SQLite file used by the firm store (default: data/company.db).
# CORPOS_DB=data/company.db

# ── HTTP ─────────────────────────────────────────────────────────────
# PORT=3000

# ── Model providers (optional; SimulationProvider used in CI) ────────
# OPENROUTER_API_KEY=
# OPENROUTER_MODEL=openrouter/owl-alpha
# OPENROUTER_REFERER=https://corpos.local
# OPENROUTER_TITLE=CorpOS
# ANTHROPIC_API_KEY=
# OPENAI_API_KEY=
# GOOGLE_API_KEY=
# GROQ_API_KEY=

# ── MCP servers ──────────────────────────────────────────────────────
# Referenced from kilo.jsonc mcp.*.headers via ${VAR} interpolation.
# MCP_TOKEN=

# ── Project ──────────────────────────────────────────────────────────
# APP_ENV=development
# LOG_LEVEL=info
# DATABASE_URL=
44 changes: 24 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,41 @@ permissions:
contents: read

jobs:
lint:
verify:
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
node-version: [22, 24]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 20
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- name: ESLint
run: npm run lint
- name: Prettier check
run: npm run format:check
- name: Activate packageManager npm
run: |
corepack enable
corepack prepare npm@10.9.2 --activate
- name: Site verify (build, typecheck, test, lint, format)
run: ./scripts/harness/verify.sh

build:
adversarial:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
node-version: [20, 22, 24]
needs: verify
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: ${{ matrix.node-version }}
node-version: 22
cache: npm
- name: Activate packageManager npm
run: |
corepack enable
corepack prepare npm@10.9.2 --activate
- run: npm ci
- name: Typecheck
run: npm run typecheck
- name: Test
run: npm test
- run: npm run build
- name: Site adversarial
run: ./scripts/harness/adversarial.sh
14 changes: 9 additions & 5 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ on:
push:
branches: [main]
paths:
- "src/**"
- "apps/**"
- "packages/**"
- "scripts/**"
- "test/**"
pull_request:
branches: [main]
paths:
- "src/**"
- "apps/**"
- "packages/**"
- "scripts/**"
- "test/**"
schedule:
- cron: "27 6 * * 1"
Expand All @@ -29,15 +33,15 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@4187e74d05793876e9989daffde9c3e66b4acd07 # v3
with:
languages: ${{ matrix.language }}
queries: +security-and-quality

- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@4187e74d05793876e9989daffde9c3e66b4acd07 # v3

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@4187e74d05793876e9989daffde9c3e66b4acd07 # v3
with:
category: "/language:${{ matrix.language }}"
30 changes: 18 additions & 12 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
name: Deploy to Fly.io

# Deploys the control plane to Fly.io when a release is published.
# Requires the FLY_API_TOKEN repo secret (create with: flyctl tokens create deploy -a <app>).
# Skips automatically if the secret is absent.
# Optional release deploy for the reference demo. Skips cleanly when
# FLY_API_TOKEN is unset. Requires fly.toml + Dockerfile.
on:
release:
types: [published]
Expand All @@ -16,20 +15,27 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Abort if Fly token not configured
if: ${{ secrets.FLY_API_TOKEN == '' }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Check Fly token
id: fly
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
run: |
echo "FLY_API_TOKEN secret is not set — skipping deploy."
echo "Create it with: flyctl tokens create deploy -a <your-app>"
exit 0
if [ -z "${FLY_API_TOKEN}" ]; then
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "FLY_API_TOKEN secret is not set — skipping deploy."
else
echo "configured=true" >> "$GITHUB_OUTPUT"
fi

# setup-flyctl only publishes @master (not version tags); pin the commit.
- name: Set up Flyctl
if: ${{ secrets.FLY_API_TOKEN != '' }}
uses: superfly/flyctl-actions/setup-flyctl@1.5
if: steps.fly.outputs.configured == 'true'
uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # master

- name: Deploy
if: ${{ secrets.FLY_API_TOKEN != '' }}
if: steps.fly.outputs.configured == 'true'
run: flyctl deploy --remote-only --strategy=rolling
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
14 changes: 8 additions & 6 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ on:
push:
branches: [main]
paths:
- "src/**"
- "apps/**"
- "packages/**"
- "scripts/**"
- "test/**"
- "package.json"
- "package-lock.json"
Expand All @@ -29,21 +31,21 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4

- name: Log in to GHCR
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Derive image metadata
id: meta
uses: docker/metadata-action@v6
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
Expand All @@ -53,7 +55,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
platforms: linux/amd64,linux/arm64
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
publish_results: true

- if: always()
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@4187e74d05793876e9989daffde9c3e66b4acd07 # v3
with:
sarif_file: results.sarif
category: scorecard
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ dist/
node_modules/
data/
docs/
_archives/
_legacy_src/
_legacy_test/
.kilo/
.playwright-mcp/
package-lock.json
Expand Down
77 changes: 7 additions & 70 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,11 @@
# Project context — loaded into every agent session via `instructions` in kilo.jsonc.
# Project context

# Keep this short and factual. It orients agents to THIS project specifically
CorpOS is the open reference implementation of an autonomous company:
firm model + work contracts + control plane. Simulation-first.

# (unlike AGENTS.md, which states universal rules).
Workspaces: `packages/core`, `packages/mcp-knowledge`, `apps/api`, `apps/console`.

## What this project is
Commands: `npm run dev`, `npm test`, `npm run scenario`, `npm run audit:verify`,
`./scripts/harness/verify.sh`, `./scripts/harness/adversarial.sh`.

`CorpOS` — an autonomous company operating system: a TypeScript multi-agent
runtime plus a control plane (policy engine, approval gates, spend caps, audit,
REST/WebSocket API, dashboard). Department "agents" (support, sales, ops,
finance, engineering) reason via an LLM, call permissioned tools, and coordinate
through a shared orchestrator. Every consequential action is policy-gated.

## Architecture (in brief)

- **Runtime layer** (`src/core/`): `types`, `llm`, `logger`, `event-bus`, `store`
(SQLite via better-sqlite3), `tool` (typed registry + permissions), `policy`
(allow/deny/approve decisions + spend tracking), `memory`, `agent` (reasoning
loop), `orchestrator` (lifecycle, dispatch, concurrency, retries), `app`
(composition root).
- **Agents** (`src/agents/`): one file per department; each declares a system
prompt, a tool subset, and owns its domain tools.
- **Tools** (`src/tools/`): CRM, comms, billing, knowledge, system packs.
Every tool declares a `ToolPermission` (category + optional cost cap +
`requiresApproval`).
- **Control plane** (`src/api/`): Express REST + WebSocket; serves the dashboard.

## Key conventions

- TypeScript, ESM (`"type": "module"`), strict. Node >= 20.
- 2-space indent, LF, no trailing whitespace (see `.editorconfig`).
- IDs via `nanoid`. JSON-schema for tool parameter validation; `zod` only where
ergonomic.
- No comments unless genuinely necessary.
- Consequential tools (spend, external comms, mutating) MUST route through the
policy engine and produce audit events. Nothing destructive runs autonomously.
- Agent reasoning loop is LLM-driven but fully reproducible under the
`SimulationProvider` (scripted responses) — all tests run without network.

## Commands

- Install: `npm install`
- Typecheck: `npm run typecheck`
- Build: `npm run build`
- Run server (API + dashboard): `npm run dev` (boots on `$PORT` or 3000)
- Run a recorded multi-agent scenario: `npm run scenario`
- Tests: `npm test` (vitest)

## LLM provider

Agents call **OpenRouter** via its OpenAI-compatible endpoint
(`https://openrouter.ai/api/v1`), model **Owl Alpha**, key from
`OPENROUTER_API_KEY` and slug from `OPENROUTER_MODEL` (default
`openrouter/owl-alpha` — set the real OpenRouter slug for Owl Alpha there).
Optional `OPENROUTER_REFERER` / `OPENROUTER_TITLE` set the OpenRouter
`HTTP-Referer` / `X-Title` headers. Z.AI and OpenAI remain supported as
alternatives (`provider: "zai"|"openai"`); the factory auto-detects by key.

**Gotcha: no key is set in this environment.** The system runs
**simulation-first** — the `SimulationProvider` returns scripted/tool-driven
responses so the full system is demonstrable and tested without network. Provide
`OPENROUTER_API_KEY` to switch the default agent provider to live OpenRouter.
Never log keys.

## Gotchas

- Do NOT edit `package-lock.json` directly (permission-denied by design); run
`npm install` to change the lockfile.
- Agent Manager run-script derives a per-worktree `$PORT` from the worktree path;
parallel worktrees bind different ports — read `$PORT`, don't hardcode 3000.
- SQLite file (`data/company.db`) is created at runtime; do not commit it.
- The reasoning loop is non-deterministic under a live LLM; deterministic tests
rely on `SimulationProvider` scripts.
Site id: `corpos`. Program: `corpos-autonomous-company`.
42 changes: 10 additions & 32 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
# CorpOS control-plane image.
#
# Runs the app under tsx (the same ESM resolver used in dev). The codebase
# uses directory/extensionless imports that tsx resolves; a compiled image
# would require a bundler step, which is out of scope for this reference.
# Simulation-first: works with zero configuration (no API key required).

FROM node:20-alpine AS base

FROM node:22-bookworm-slim
WORKDIR /app

# Install dependencies first for better layer caching.
COPY package.json package-lock.json ./
RUN npm ci && npm cache clean --force

# Copy the application source (dashboard + runtime + tools + tests).
COPY . .

# SQLite writes here at runtime; ensure the non-root user owns it.
RUN mkdir -p data && chown -R node:node /app

ENV NODE_ENV=production \
PORT=3000 \
LOG_LEVEL=info

USER node

COPY package.json package-lock.json* .npmrc* ./
COPY packages ./packages
COPY apps ./apps
COPY scripts ./scripts
COPY tsconfig*.json vitest.config.ts eslint.config.mjs ./
RUN corepack enable && corepack prepare npm@10.9.2 --activate \
&& npm ci && npm run build
ENV PORT=3000
EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- "http://127.0.0.1:${PORT:-3000}/api/health" >/dev/null 2>&1 || exit 1

# No API key set → simulation mode. Set OPENROUTER_API_KEY (etc.) to go live.
CMD ["npx", "tsx", "src/index.ts"]
CMD ["npm", "run", "start"]
Loading
Loading