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
83 changes: 83 additions & 0 deletions examples/multi-agent-showcase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# OBJ-09: Signature Multi-Agent Showcase

This showcase demonstrates a full WorkGraph collaboration lifecycle with three agents:

- `governance-admin` (governance + approvals)
- `agent-intake` (triage + routing)
- `agent-builder` (implementation)
- `agent-reviewer` (self-assembly + QA closure)

The flow is intentionally end-to-end and reproducible from a fresh workspace. Every WorkGraph CLI invocation uses `--json`.

## What this demonstrates

1. **Agent registration and governance**
- Bootstrap admin registration
- Approval-based registration requests for agents
- Credential issuance and heartbeat publication

2. **Thread lifecycle and plan-step coordination**
- Multi-thread objective decomposition
- Conversation and plan-step creation
- Claim/start/progress/done transitions across multiple actors

3. **Self-assembly**
- Capability advertisement + requirements matching
- `assembleAgent()` claims the next suitable thread
- Existing plan-step is automatically activated for the assembled agent

4. **Trigger -> run -> evidence loop**
- Trigger creation with a structured `dispatch-run` action
- Trigger engine cycle executes runs automatically
- Dispatch run evidence chain is validated from CLI output
- Ledger hash-chain integrity is verified

## Run it

From repo root:

```bash
bash examples/multi-agent-showcase/run.sh --json
```

Optional arguments:

- `--workspace <path>`: use a specific workspace directory
- `--skip-build`: skip `pnpm run build` (useful in tests)
- `--json`: emit machine-readable summary only

## Script breakdown

- `scripts/01-governance.mjs`
- Initializes workspace
- Registers `governance-admin` with bootstrap token
- Runs request/review approval flow for all collaborating agents
- Outputs issued API keys and governance snapshot

- `scripts/02-collaboration.mjs`
- Creates threads, conversation, and plan-steps
- Drives intake + builder thread lifecycle transitions
- Runs self-assembly for reviewer via SDK
- Completes reviewer plan-step and closes conversation

- `scripts/03-trigger-loop.mjs`
- Creates active trigger via SDK with `dispatch-run` action
- Executes trigger engine loop with run execution
- Validates run status/evidence and ledger integrity

- `scripts/run-showcase.mjs`
- Orchestrates all phases
- Collects rollup metrics and boolean capability checks
- Returns one final JSON report

## Expected outcome

The final JSON output contains:

- `checks.governance`
- `checks.selfAssemblyClaimedReviewerThread`
- `checks.planStepCoordinated`
- `checks.triggerRunEvidence`
- `checks.ledgerActivity`

When all checks are `true`, the showcase has completed successfully.
58 changes: 58 additions & 0 deletions examples/multi-agent-showcase/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"

WORKSPACE=""
SKIP_BUILD=0
JSON_MODE=0

while [[ $# -gt 0 ]]; do
case "$1" in
--workspace|-w)
if [[ $# -lt 2 ]]; then
echo "Missing value for $1" >&2
exit 1
fi
WORKSPACE="$2"
shift 2
;;
--skip-build)
SKIP_BUILD=1
shift
;;
--json)
JSON_MODE=1
shift
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: run.sh [--workspace <path>] [--skip-build] [--json]" >&2
exit 1
;;
esac
done

if [[ -z "${WORKSPACE}" ]]; then
WORKSPACE="$(mktemp -d /tmp/workgraph-obj09-showcase-XXXXXX)"
fi

if [[ "${SKIP_BUILD}" -ne 1 ]]; then
echo "[obj-09] building repository artifacts..." >&2
(
cd "${REPO_ROOT}"
pnpm run build >/dev/null
)
fi

SHOWCASE_ARGS=(--workspace "${WORKSPACE}" --json)
if [[ "${SKIP_BUILD}" -eq 1 ]]; then
SHOWCASE_ARGS+=(--skip-build)
fi

if [[ "${JSON_MODE}" -ne 1 ]]; then
echo "[obj-09] running showcase in ${WORKSPACE}" >&2
fi

node "${SCRIPT_DIR}/scripts/run-showcase.mjs" "${SHOWCASE_ARGS[@]}"
165 changes: 165 additions & 0 deletions examples/multi-agent-showcase/scripts/01-governance.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env node

import path from 'node:path';
import {
ensureBuild,
logLine,
resolveRepoRoot,
resolveWorkspace,
runCliJson,
} from './lib/demo-utils.mjs';

const AGENTS = {
admin: 'governance-admin',
intake: 'agent-intake',
builder: 'agent-builder',
reviewer: 'agent-reviewer',
};

const roleByAgent = {
[AGENTS.intake]: 'roles/contributor.md',
[AGENTS.builder]: 'roles/contributor.md',
[AGENTS.reviewer]: 'roles/viewer.md',
};

async function main() {
const repoRoot = resolveRepoRoot(import.meta.url);
const resolved = resolveWorkspace(process.argv.slice(2));
if (!resolved.skipBuild) {
logLine('building dist artifacts', resolved.json);
await ensureBuild(repoRoot);
}
const workspacePath = resolved.workspacePath;

logLine('initializing workspace', resolved.json);
const init = await runCliJson(repoRoot, ['init', workspacePath, '--json']);
const bootstrapTrustToken = String(init.data.bootstrapTrustToken);

logLine('registering governance admin', resolved.json);
const adminRegistration = await runCliJson(repoRoot, [
'agent',
'register',
AGENTS.admin,
'-w',
workspacePath,
'--token',
bootstrapTrustToken,
'--role',
'roles/admin.md',
'--capabilities',
'policy:manage,agent:approve-registration,agent:register,dispatch:run,thread:claim,thread:manage',
'--actor',
AGENTS.admin,
'--json',
]);
const adminApiKey = String(adminRegistration.data.apiKey ?? '');

const approvals = [];
for (const agent of [AGENTS.intake, AGENTS.builder, AGENTS.reviewer]) {
logLine(`requesting registration for ${agent}`, resolved.json);
const request = await runCliJson(
repoRoot,
[
'agent',
'request',
agent,
'-w',
workspacePath,
'--actor',
agent,
'--role',
roleByAgent[agent],
'--capabilities',
'thread:claim,thread:manage,dispatch:run,agent:heartbeat',
'--note',
`OBJ-09 demo onboarding for ${agent}`,
'--json',
],
{ env: adminApiKey ? { WORKGRAPH_API_KEY: adminApiKey } : undefined },
);
const requestPath = String(request.data.request.path);

logLine(`approving registration for ${agent}`, resolved.json);
const review = await runCliJson(
repoRoot,
[
'agent',
'review',
requestPath,
'-w',
workspacePath,
'--decision',
'approved',
'--actor',
AGENTS.admin,
'--role',
roleByAgent[agent],
'--capabilities',
'thread:claim,thread:manage,dispatch:run,agent:heartbeat',
'--json',
],
{ env: adminApiKey ? { WORKGRAPH_API_KEY: adminApiKey } : undefined },
);
approvals.push({
agent,
requestPath,
approvalPath: String(review.data.approval.path),
apiKey: String(review.data.apiKey ?? ''),
});
}

logLine('publishing initial agent heartbeats', resolved.json);
for (const approval of approvals) {
await runCliJson(
repoRoot,
[
'agent',
'heartbeat',
approval.agent,
'-w',
workspacePath,
'--actor',
approval.agent,
'--status',
'online',
'--capabilities',
'thread:claim,thread:manage,dispatch:run,agent:heartbeat',
'--json',
],
{ env: approval.apiKey ? { WORKGRAPH_API_KEY: approval.apiKey } : undefined },
);
}

const agents = await runCliJson(
repoRoot,
['agent', 'list', '-w', workspacePath, '--json'],
{ env: adminApiKey ? { WORKGRAPH_API_KEY: adminApiKey } : undefined },
);
const credentials = await runCliJson(
repoRoot,
['agent', 'credential-list', '-w', workspacePath, '--json'],
{ env: adminApiKey ? { WORKGRAPH_API_KEY: adminApiKey } : undefined },
);

const output = {
workspacePath,
bootstrapTrustToken,
admin: {
actor: AGENTS.admin,
apiKey: adminApiKey,
credentialId: String(adminRegistration.data.credential?.id ?? ''),
},
approvals,
governanceSnapshot: {
agentCount: Number(agents.data.count ?? 0),
credentialCount: Number(credentials.data.count ?? 0),
},
};
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
}

main().catch((error) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exit(1);
});
Loading
Loading