Skip to content
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3290b88
Add agent testing harness for skill verification (#2921)
Myestery Mar 5, 2026
4ec5a96
Fix remaining harness issues: assertion engine, settings, deploy, inv…
Myestery Mar 9, 2026
d7cb9b1
Move golem-new-project skill to golem/skills/, remove adding-dependen…
Myestery Mar 9, 2026
822ee01
Merge branch 'main' into skills
Myestery Mar 9, 2026
f7df973
Fix Gemini agent driver and skill activation detection
Myestery Mar 10, 2026
9959988
Add opencode agent to CI, uncomment Gemini install, update paths
Myestery Mar 10, 2026
3336131
Add template variables, conditional steps, dry-run, abort handling, a…
Myestery Mar 10, 2026
1da3c87
Merge branch 'main' into skills
Myestery Mar 11, 2026
9125b2e
Add --help flag to harness run.ts (#2871)
Myestery Mar 12, 2026
3c516ec
Extract skill symlink logic into BaseAgentDriver.linkSkills()
Myestery Mar 12, 2026
f35aa63
Add Codex agent driver and default --scenarios flag
Myestery Mar 12, 2026
ade2e95
Update golem-new-project skill to check golem/golem-cli availability
Myestery Mar 12, 2026
ab1beb0
Fix Gemini CI failure and add Codex to CI matrix
Myestery Mar 13, 2026
81e6fb4
Add GOOGLE_GENERATIVE_AI_API_KEY for OpenCode in CI
Myestery Mar 13, 2026
e1ec14b
Add codex login step in CI using --with-api-key
Myestery Mar 13, 2026
9c05312
Run harness tests in isolated directory outside the repo
Myestery Mar 13, 2026
5ff705c
Fix git init in CI isolated dir: set user identity and branch name
Myestery Mar 13, 2026
e037b22
Fix isolated dir: copy dirs individually, use global tsx
Myestery Mar 13, 2026
689bcc7
Move skills/ and tests/harness/ into golem-skills/ directory
Myestery Mar 16, 2026
aaabacc
Strip inline ticket references from code comments
Myestery Mar 16, 2026
cb61348
Add discriminated union validation for StepSpec
Myestery Mar 16, 2026
00b9e31
Make --agent and --language optional, default to all
Myestery Mar 16, 2026
275a002
Expand golem-new-project-ts scenario with deploy and shell steps
Myestery Mar 16, 2026
0fd1472
Extract DEFAULT_STEP_TIMEOUT_SECONDS constant
Myestery Mar 16, 2026
58b9e96
Format code with prettier
Myestery Mar 16, 2026
f632aae
Remove deploy step from golem-new-project-ts scenario
Myestery Mar 17, 2026
2fdb7e6
Merge remote-tracking branch 'origin/skills' into harness-enhancements
Myestery Mar 17, 2026
30f42f8
Add template variables, conditional steps, dry-run, abort handling, a…
Myestery Mar 18, 2026
38deb4c
Merge remote-tracking branch 'origin/harness-enhancements' into imple…
Myestery Mar 20, 2026
dd1a875
Merge remote-tracking branch 'origin/main' into implement-missing-har…
Myestery Mar 20, 2026
9c118c0
Add HTTP steps, retry logic, resume-from, failure classification, and…
Myestery Mar 20, 2026
7692c84
Add Postgres and language toolchain setup to CI workflow
Myestery Mar 20, 2026
f2e29ec
Merge remote-tracking branch 'origin/main' into implement-missing-har…
Myestery Mar 20, 2026
f83cce7
rebuild golem artifact
Myestery Mar 23, 2026
0839d37
rebuild golem artifact
Myestery Mar 23, 2026
7752751
ci: Install Golem binaries from workflow artifacts instead of direct …
Myestery Mar 23, 2026
71eb392
Use golem binary artifacts from build workflow instead of v1.4.2 release
Myestery Mar 23, 2026
0df7fa8
ci: Set GOLEM_PATH in skills-test workflow for local SDK overrides
Myestery Mar 23, 2026
0d9ff14
Update scenario prompt to set GOLEM_TS_PACKAGES_PATH before golem new
Myestery Mar 23, 2026
7f61566
Merge remote-tracking branch 'origin/main' into implement-missing-har…
Myestery Mar 23, 2026
9224cb7
Update scenario prompt to fix SDK versions to 1.0.0-dev.1 after proje…
Myestery Mar 23, 2026
70c1698
Add golem-db-app skill and scenario with HTTP + DB verification
Myestery Mar 23, 2026
df25c8e
Allow golem-db-app as extra skill in golem-new-project-ts scenario
Myestery Mar 23, 2026
e9eb72f
Add SDK version fix instruction to golem-db-app-ts scenario prompt
Myestery Mar 23, 2026
38dfb10
Remove SDK version fix from db-app scenario — GOLEM_PATH provides com…
Myestery Mar 24, 2026
a29ebcb
ci: Build TS SDK before skill tests so GOLEM_PATH local refs work
Myestery Mar 24, 2026
4ef971f
ci: Install wasm-rquickjs-cli before building TS SDK
Myestery Mar 24, 2026
cc11d0e
Merge branch 'main' into implement-missing-harness-issues
Myestery Mar 25, 2026
97de1bb
ci: Build agent_guest.wasm for TS SDK — install cargo-component and w…
Myestery Mar 25, 2026
d87b71e
ci: Use cargo make build-sdk-ts for TS SDK build in skill tests
Myestery Mar 25, 2026
176abed
Clean golem-temp before verification deploy to avoid stale cached art…
Myestery Mar 25, 2026
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
83 changes: 83 additions & 0 deletions .github/workflows/build-golem-binaries.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Build Golem Binaries

on:
workflow_dispatch:
push:
branches:
- '**'

concurrency:
group: build-golem-binaries-${{ github.ref }}
cancel-in-progress: true

permissions:
actions: write
contents: read

jobs:
check-trigger:
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 1
- name: Check trigger condition
id: check
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "should_run=true" >> "$GITHUB_OUTPUT"
else
MSG=$(git log -1 --pretty=%s)
if echo "$MSG" | grep -q "rebuild golem artifact"; then
echo "should_run=true" >> "$GITHUB_OUTPUT"
else
echo "should_run=false" >> "$GITHUB_OUTPUT"
fi
fi

build:
needs: check-trigger
if: needs.check-trigger.outputs.should_run == 'true'
env:
CARGO_BUILD_JOBS: 20
runs-on: blacksmith-32vcpu-ubuntu-2204
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 1

# --- Rust toolchain (matches ci.yaml build-and-store + publish-binaries) ---
- name: Setup Rust
run: rustup update stable --no-self-update && rustup default stable && rustup target add wasm32-wasip1 wasm32-wasip2
- uses: Swatinem/rust-cache@v2
with:
prefix-key: v4-rust
shared-key: release
cache-all-crates: true
save-if: true
- uses: davidB/rust-cargo-make@v1
# --- Build golem-cli and golem server (release, matches publish-binaries) ---
- name: Purge v8 from cache
run: cargo make --profile ci clear-v8

- name: Build golem-cli and golem server
run: cargo build -p golem-cli -p golem --release
timeout-minutes: 30

# --- Upload artifacts ---
- name: Upload golem-cli
uses: actions/upload-artifact@v4
with:
name: golem-cli
path: target/release/golem-cli
retention-days: 30

- name: Upload golem server
uses: actions/upload-artifact@v4
with:
name: golem-server
path: target/release/golem
retention-days: 30
47 changes: 43 additions & 4 deletions .github/workflows/skills-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ jobs:
name: ${{ matrix.agent }} x ${{ matrix.language }}
runs-on: ubuntu-latest
needs: unit-tests
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: golem_test
ports:
- "5432:5432"
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: golem
MYSQL_DATABASE: golem_test
ports:
- "3306:3306"
options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: false
matrix:
Expand All @@ -68,12 +86,30 @@ jobs:
with:
targets: wasm32-wasip2

- name: Download golem-cli artifact
uses: dawidd6/action-download-artifact@v6
with:
workflow: build-golem-binaries.yaml
name: golem-cli
path: /tmp/golem-bin
search_artifacts: true
check_artifacts: true

- name: Download golem-server artifact
uses: dawidd6/action-download-artifact@v6
with:
workflow: build-golem-binaries.yaml
name: golem-server
path: /tmp/golem-bin
search_artifacts: true
check_artifacts: true

- name: Install Golem
run: |
curl -Lo golem https://github.com/golemcloud/golem/releases/download/v1.4.2/golem-x86_64-unknown-linux-gnu
chmod +x golem
sudo mv golem /usr/local/bin/golem
golem --version
chmod +x /tmp/golem-bin/golem-cli /tmp/golem-bin/golem
sudo mv /tmp/golem-bin/golem-cli /usr/local/bin/golem-cli
sudo mv /tmp/golem-bin/golem /usr/local/bin/golem
golem-cli --version || golem --version

- name: Start Golem Server
run: |
Expand Down Expand Up @@ -136,6 +172,9 @@ jobs:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
DATABASE_URL: postgres://golem:golem@localhost:5432/golem_test
MYSQL_URL: mysql://root:golem@localhost:3306/golem_test
GOLEM_PATH: ${{ github.workspace }}
run: |
cd /tmp/harness-run
tsx src/run.ts --agent ${{ matrix.agent }} --language ${{ matrix.language }} --scenarios scenarios --skills ./skills
Expand Down
171 changes: 171 additions & 0 deletions golem-skills/skills/golem-db-app/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
name: golem-db-app
description: "Building a Golem application with PostgreSQL database integration. Use when creating agents that store and query data using golem:rdbms/postgres, defining HTTP endpoints, and configuring environment variables."
---

# Building a Database-Backed Golem Application

**Important: Do not try to build golem from scratch or install it manually.**

Assume the `golem` or `golem-cli` binary exists and is added to PATH.
Try `golem --version` to check if it exists. If not, try `golem-cli --version`.

## Step 1: Create the Project

```shell
golem new <APP_NAME> --template ts -Y
```

## Step 2: Determine the rdbms Module Version

Check which rdbms version the installed SDK provides:

```shell
ls node_modules/@golemcloud/golem-ts-sdk/types/ | grep rdbms
```

This will show files like `golem_rdbms_X_Y_Z_postgres.d.ts`. Use that version in your import (e.g., `@1.5.0` or `@0.0.2`).

## Step 3: Write the Agent with PostgreSQL

Import the database module and define an agent with HTTP endpoints. Use the rdbms version found in Step 2:

```typescript
import {
BaseAgent,
agent,
prompt,
description,
endpoint
} from '@golemcloud/golem-ts-sdk';
// Use the version from Step 2 (check node_modules/@golemcloud/golem-ts-sdk/types/)
import { DbConnection, DbValue, DbResult } from 'golem:rdbms/postgres@1.5.0';

@agent({
mount: "/items/{name}"
})
export class ItemAgent extends BaseAgent {
private readonly name: string;
private initialized: boolean = false;

constructor(name: string) {
super();
this.name = name;
}

private getDb(): DbConnection {
return DbConnection.open(process.env.DB_POSTGRES_URL!);
}

private ensureTable(): void {
if (!this.initialized) {
const db = this.getDb();
db.execute(
"CREATE TABLE IF NOT EXISTS items (id SERIAL PRIMARY KEY, name TEXT NOT NULL)",
[]
);
this.initialized = true;
}
}

@endpoint({ post: "/add" })
async addItem(name: string): Promise<string> {
this.ensureTable();
const db = this.getDb();
db.execute(
"INSERT INTO items (name) VALUES ($1)",
[{ tag: 'text', val: name } as DbValue]
);
return `Added: ${name}`;
}

@endpoint({ get: "/list" })
async listItems(): Promise<string[]> {
this.ensureTable();
const db = this.getDb();
const result: DbResult = db.query("SELECT name FROM items", []);
return result.rows.map(row => {
const val = row.values[0];
return val.tag === 'text' ? val.val : String(val.val);
});
}
}
```

### DbValue Tagged Union

All query parameters use the `DbValue` tagged union. Common tags:

| TypeScript | DbValue |
|-----------|---------|
| `string` | `{ tag: 'text', val: "hello" }` |
| `number` (int) | `{ tag: 'int4', val: 42 }` |
| `number` (float) | `{ tag: 'float8', val: 3.14 }` |
| `boolean` | `{ tag: 'boolean', val: true }` |
| `bigint` | `{ tag: 'int8', val: 100n }` |
| `null` | `{ tag: 'null' }` |

### DbResult Structure

```typescript
const result: DbResult = db.query("SELECT id, name FROM items", []);
// result.columns: DbColumn[] — column metadata
// result.rows: DbRow[] — array of rows
// result.rows[0].values: DbValue[] — values in column order
```

### Transactions

```typescript
const db = this.getDb();
const tx = db.beginTransaction();
try {
tx.execute("INSERT INTO items (name) VALUES ($1)", [{ tag: 'text', val: "a" }]);
tx.execute("INSERT INTO items (name) VALUES ($1)", [{ tag: 'text', val: "b" }]);
tx.commit();
} catch (e) {
tx.rollback();
throw e;
}
```

## Step 4: Export the Agent

In `src/main.ts`:

```typescript
export { ItemAgent } from './item-agent';
```

## Step 5: Configure Environment Variables

In `golem.yaml`, add the database URL under the component's `env` section:

```yaml
components:
<app-name>:ts-main:
env:
DB_POSTGRES_URL: "postgres://golem:golem@localhost:5432/golem_test"
```

The component name follows the pattern `<app-name>:ts-main` for single-component TypeScript apps.

## Step 6: Build and Deploy

```shell
cd <APP_NAME>
golem build
golem deploy --yes
```

After deployment, HTTP endpoints are available at `http://<app-name>.localhost:9006/<mount-path>/<endpoint-path>`.

## Checklist

1. `golem new` executed successfully
2. SDK versions fixed to `1.0.0-dev.1` in `package.json`
3. Agent imports `DbConnection` from `'golem:rdbms/postgres@1.5.0'`
4. Agent uses `@agent({ mount: "..." })` and `@endpoint({ get/post: "..." })`
5. `DB_POSTGRES_URL` set in `golem.yaml` under component env
6. `golem build` succeeds
7. `golem deploy --yes` succeeds
67 changes: 67 additions & 0 deletions golem-skills/tests/harness/scenarios/golem-db-app-ts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: "golem-db-app-ts"
settings:
timeout_per_subprompt: 600
golem_server:
custom_request_port: 9006
steps:
- id: "create-db-app"
prompt: |
Create a new Golem application called db-app with TypeScript.
After creating the project, fix SDK versions: replace any local path references
to golem-ts-sdk and golem-ts-typegen in package.json with version 1.0.0-dev.1,
then run npm install.

Then modify the app to be a simple task tracker backed by PostgreSQL:
1. Create a TaskAgent with mount path "/tasks/{name}" that:
- Has a POST endpoint at "/add" that takes a title (string) parameter, inserts it into a "tasks" table, and returns a confirmation message
- Has a GET endpoint at "/list" that returns all task titles from the table as a string array
- On first call, creates the tasks table if it doesn't exist (id SERIAL PRIMARY KEY, title TEXT NOT NULL)
2. Check node_modules/@golemcloud/golem-ts-sdk/types/ for the rdbms version (e.g. golem_rdbms_X_Y_Z_postgres.d.ts) and use that version in the import: import { DbConnection, DbValue, DbResult } from 'golem:rdbms/postgres@X.Y.Z'
3. Read the DB connection URL from process.env.DB_POSTGRES_URL
4. Set DB_POSTGRES_URL environment variable in golem.yaml under the component's env section to "postgres://postgres:postgres@localhost:5432/golem_test"

Build the project with golem build, then deploy with golem deploy --yes.
expectedSkills:
- "golem-db-app"
allowedExtraSkills:
- "golem-new-project"
verify:
deploy: true

- id: "check-project-files"
shell:
command: "ls"
args: ["db-app/golem.yaml"]
expect:
exit_code: 0

- id: "add-task-via-http"
http:
url: "http://db-app.localhost:9006/tasks/default/add"
method: "POST"
headers:
Content-Type: "application/json"
body: '{"title": "Test task from harness"}'
expect:
status: 200
retry:
attempts: 3
delay: 5

- id: "list-tasks-via-http"
http:
url: "http://db-app.localhost:9006/tasks/default/list"
method: "GET"
expect:
status: 200
body_contains: "Test task from harness"

- id: "verify-db-rows"
shell:
command: "bash"
args:
- "-c"
- "PGPASSWORD=postgres psql -h localhost -U postgres -d golem_test -t -c \"SELECT count(*) FROM tasks WHERE title = 'Test task from harness'\" | tr -d ' '"
expect:
exit_code: 0
stdout_matches: "[1-9]"
Loading
Loading