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
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,70 @@ jobs:

- name: Test
run: npm test

- name: Execution and recovery regressions
run: npm run test:focused

- name: Compiler parity
run: npm run verify:typescript

- name: Packed consumer and compiled grant CLI
run: npm run test:package && npm run test:work-cli

native-hosts:
runs-on: ubuntu-latest
strategy:
matrix:
node: [22, 24]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run build
- name: Install pinned native plugin SDKs
run: |
npm ci --ignore-scripts --prefix plugins/openclaw-execufunction
npm ci --ignore-scripts --prefix plugins/openclaw-openfunctions
npm ci --ignore-scripts --prefix plugins/pi-openfunctions
- name: Real SDK types manifests loaders and bounded work
run: npm run test:hosts

hermes:
runs-on: ubuntu-latest
strategy:
matrix:
node: [22, 24]
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: NousResearch/hermes-agent
ref: 7cc3fb8cb55898747dd1dfa56fda3e16ebba028e
path: .host-fixtures/hermes-agent
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
cache: npm
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- run: npm ci
- name: Provision the official managed Hermes interpreter
run: |
node test-client/provision-hermes-runtime.mjs --hermes-root "$GITHUB_WORKSPACE/.host-fixtures/hermes-agent" --runtime-dir "$RUNNER_TEMP/hermes-runtime" --data-dir "$RUNNER_TEMP/hermes-provision-home" --out "$RUNNER_TEMP/hermes-runtime.json"
- name: Real portable host grants execution and crash recovery
run: |
HERMES_TEST_PYTHON=$(node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).python)' "$RUNNER_TEMP/hermes-runtime.json")
npm run test:hermes -- --hermes-root "$GITHUB_WORKSPACE/.host-fixtures/hermes-agent" --python "$HERMES_TEST_PYTHON" --runtime-dir "$RUNNER_TEMP/hermes-runtime" --mode work --install-mode online --out "$RUNNER_TEMP/hermes-evidence"
- name: Retain host evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: hermes-node-${{ matrix.node }}
path: |
${{ runner.temp }}/hermes-runtime.json
${{ runner.temp }}/hermes-evidence
if-no-files-found: warn
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dist/
!.env.example
.DS_Store
.data/
.host-fixtures/

# OpenMemory - IDE/Assistant specific rules
.windsurf/rules/openmemory.md
Expand Down
23 changes: 17 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ src/
context.ts # Context provider interface — connectProvider(), contextPrompt()
server.ts # startServer() — MCP server wrapper
types.ts # TypeScript interfaces
index.ts # Re-exports everything
index.ts # Side-effect-free public framework exports
executor.ts # Canonical validation, policy, scheduling and receipts
execution-journal.ts # Standalone durable action evidence
work/ # Opt-in bounded attempts, grants, leases and verification
providers/ # Context provider implementations
execufunction/ # ExecuFunction reference provider (tasks, calendar, CRM, etc.)
examples/ # Read these for patterns
Expand All @@ -37,7 +40,9 @@ src/
utilities/ # Stateless helpers — no store needed (beginner)
my-tools/ # Student builds here
index.ts # Add tools to the myTools array
index.ts # Entry point — registers all tools, starts MCP server
index.ts # Side-effect-free package library entry
workshop.ts # Source workshop — registers tools and starts MCP
cli.ts # Compiled npm executable; serve/config/work commands
test-client/
cli.ts # Interactive CLI for testing tools (npm run test-tools)
run-tests.ts # Tool-test runner — runs `tests` arrays on tool defs
Expand Down Expand Up @@ -110,7 +115,7 @@ examples for stateless tools.

1. Define tools in `src/my-tools/index.ts`
2. Add them to the `myTools` array export
3. They are automatically registered by `src/index.ts`
3. They are automatically registered by `src/workshop.ts` through `register-tools.ts`
4. Test with `npm run test-tools` or `npm run dev`

## Framework Exports
Expand Down Expand Up @@ -158,6 +163,9 @@ npm run dev # Auto-restarts on save (tsx watch)
npm run inspect # MCP Inspector web UI
npm run gemini # Chat with Gemini using your tools (needs GEMINI_API_KEY)
npm start # Start MCP server for Claude Desktop
npm run typecheck # Check development/test types
npm run test:focused # Local execution, journal, policy and recovery regressions
npm run test:package # Fresh packed consumer and declarations
```

## Ralph Loops
Expand Down Expand Up @@ -201,9 +209,12 @@ agent's output. Returns `CrewResult` with a `ralph` summary attached.

### Siftable provider (`src/providers/execufunction/`)

Wraps `@siftable/mcp-server@^1.2.9`. Exports `createSiftableProvider()`
(and a deprecated `createExecuFunctionProvider` alias). 31 tools across
10 domains today; ~80 more SDK methods reachable via `client.raw()`.
Uses optional peer `@siftable/mcp-server@^1.5.1` with exact dev SDK 1.5.1.
Exports `createSiftableProvider()` and deprecated `createExecuFunctionProvider`.
SDK feature gates and transport policy determine the catalog; unsupported
hosted work sessions are excluded from embedded discovery. Hosted MCP is
separate; its durable controller remains blocked by documented public contract
gaps. Never invent principals, receipt lookup, or retry guarantees.

Auth resolution: explicit option → `SIFT_PAT` env → `EXF_PAT` env.
Same chain for `SIFT_API_URL` and `SIFT_WORKSPACE_ID`.
Expand Down
89 changes: 78 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

openFunctions is an MIT-licensed TypeScript framework for building AI-callable tools and exposing them through [MCP](https://modelcontextprotocol.io), chat adapters, workflows, and agents. Its core runtime is simple:

`ToolDefinition -> ToolRegistry -> AIAdapter`
`ToolDefinition -> ToolRegistry -> ToolExecutor -> adapter or host`

Everything else composes on top of that:

Expand Down Expand Up @@ -56,6 +56,66 @@ npm run test-tools

The first thing to build is a tool, not an agent.

### Use the compiled library

The package root is a library. Importing it does not load `.env`, register
workshop tools, create stores, or start a server. Build and pack this checkout
to try the revised package before publication:

```sh
npm run build
npm pack
# In a separate consumer:
npm install /absolute/path/to/openfunction-1.0.0.tgz
```

```ts
import { defineTool, ToolRegistry, ok } from "openfunction/framework/core";

const registry = new ToolRegistry();
registry.register(defineTool({
name: "hello_world",
description: "Return a greeting for the supplied name",
inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] },
handler: async ({ name }) => ok({ greeting: `Hello ${name}` }),
}));
export { registry };
```

Serve an exported registry with `openfunction serve --module
/absolute/path/to/tools.js`. The existing `npm start`, `npm run dev`, and
workshop commands still use the source workshop entry. Siftable and PostgreSQL
are optional peers; root, core, and framework imports work without either.

### Complete bounded work through Hermes

The opt-in work profile selects a finite trusted step catalog, caller-owned
state and grants, an explicit local work item, and exact verification bindings.
The compiled CLI reviews and issues authority separately from Hermes's tool
approval gate:

```sh
openfunction work prepare --config /absolute/profile.json --work-item job --out /absolute/preview.json
openfunction work authorize --config /absolute/profile.json --preview /absolute/preview.json
openfunction hermes-config --config /absolute/profile.json
openfunction serve --profile work --config /absolute/profile.json
```

Authorization displays the bound scope and limits. Type `AUTHORIZE` to permit
execution; automatic completion stays off unless you separately type
`COMPLETE`. Changes to the configuration, contract, or plan invalidate the
preview. Grant revocation takes effect at the next admission and completion.
See [runtime integrations](docs/AGENT-RUNTIMES.md) for profile configuration,
portable installation, and the verified host commands.

The controller records effects and independent verification, renews a bounded
lease during long steps, and refuses completion with stale proof or unknown
effects. It covers actions performed through its executor. Native host actions
remain outside that journal. Local grant files assume trusted same-user code;
they provide no sandbox against a process that can modify their custody.
Hosted Siftable calls use authenticated MCP, but its durable-work backend is
disabled until the missing public recovery and parser contracts are available.

## The Mental Model

A tool is your business logic plus a schema the AI can read:
Expand Down Expand Up @@ -344,10 +404,11 @@ The provider projects its default tool set directly from the published
[`@siftable/mcp-server`](https://www.npmjs.com/package/@siftable/mcp-server)
SDK. That keeps tool names, schemas, feature gates, transport containment,
work-item lifecycle rules, structured receipts, and execution behavior aligned
with the installed Siftable MCP version. The current `1.2.27` package declares
136 tools; 114 are callable with its default feature flags. Optional dataset
and ontology tools appear automatically when their Siftable feature flags are
enabled. Set `includeLegacyAliases: true` only when migrating callers that still
with the installed Siftable MCP version. The development SDK is pinned to
`1.5.1`; drift checks compare its actual enabled catalog and schemas. Hosted
session tools require custody unavailable to this embedded provider and are
excluded. Optional dataset and ontology tools follow their Siftable feature
flags. Set `includeLegacyAliases: true` only when migrating callers that still
invoke the old hand-written `exf_*` names.

```ts
Expand Down Expand Up @@ -399,10 +460,12 @@ adds an OpenClaw runtime dependency to the framework.

Two reference plugins live in `plugins/`:

- **`openclaw-execufunction/`** — Siftable for OpenClaw. It derives all
currently enabled tools from `@siftable/mcp-server`, delegates execution to
the SDK, and generates its static `contracts.tools` manifest from the same
source. Plugin id `execufunction` remains for config compatibility.
- **`openclaw-execufunction/`** — Mixed Siftable and opt-in bounded-work plugin.
It projects the supported embedded SDK catalog through the shared executor,
resolves credentials only on invocation, and snapshots exact tool contracts.
Plugin id `execufunction` remains for config compatibility. Its legacy mixed
entry uses the SDK registration path; the static validator targets the
separate tool-only plugin below.
- **`openclaw-openfunctions/`** — Current tool-only reference using
`defineToolPlugin()`, compiled ESM, and generated manifest metadata. Marked
`private:true`; it imports the colocated framework by relative path.
Expand Down Expand Up @@ -432,11 +495,15 @@ openFunctions/
│ │ └── execufunction/ # Siftable context provider (wraps @siftable/mcp-server)
│ ├── examples/ # Reference tool patterns
│ ├── my-tools/ # Your tools
│ └── index.ts # MCP entrypoint
│ ├── runtime/ # Trusted bounded-work profile loader
│ ├── cli.ts # Compiled serve and grant commands
│ ├── workshop.ts # Source workshop MCP entrypoint
│ └── index.ts # Side-effect-free library entrypoint
├── plugins/
│ ├── openclaw-execufunction/ # Siftable plugin for openclaw (publishable)
│ ├── openclaw-openfunctions/ # Reference: current tool-plugin bridge (private)
│ └── pi-openfunctions/ # Reference: native Pi extension bridge (private)
│ ├── pi-openfunctions/ # Reference: native Pi extension bridge (private)
│ └── hermes-openfunctions/ # Portable MCP plugin and bounded-work skill
├── docs/ # Architecture docs
├── scripts/ # chat, create-tool, docs
├── test-client/ # CLI tester + test runner
Expand Down
Loading
Loading