Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
934dcf6
Add public Python SDK for custom agent harnesses
KAVentures Sep 27, 2026
7ec01f2
Add dependency-free TypeScript and Node harness client
KAVentures Sep 27, 2026
e668d4f
Fix TypeScript tool wrapper success telemetry
KAVentures Sep 27, 2026
15db1cb
Add TypeScript declarations for custom harness SDK
KAVentures Sep 27, 2026
eba9833
Make custom harness SDK sources packageable
KAVentures Sep 27, 2026
c2aa739
Expose standalone Python harness SDK
KAVentures Sep 27, 2026
79015c0
Add standalone stdlib-only Python harness SDK
KAVentures Sep 27, 2026
7d2b194
Use standalone public harness SDK implementation
KAVentures Sep 27, 2026
606fcf0
Test standalone custom harness Python SDK
KAVentures Sep 27, 2026
5ce9fdc
Test TypeScript custom harness SDK privacy contract
KAVentures Sep 27, 2026
ff367d7
Make Python agent helper independently installable
KAVentures Sep 27, 2026
7b08535
Add standalone Node and TypeScript SDK package metadata
KAVentures Sep 27, 2026
2cdb7e6
Add custom harness setup control plane
KAVentures Sep 27, 2026
26ee286
Add Connect your own agent dashboard flow
KAVentures Sep 27, 2026
f45d4a2
Register custom harness setup control plane
KAVentures Sep 27, 2026
555cd5b
Test custom harness setup permission separation
KAVentures Sep 27, 2026
d45c873
Document arbitrary agent and harness integration
KAVentures Sep 27, 2026
c5cd4a8
Make standalone Python SDK shutdown admission race-safe
KAVentures Sep 27, 2026
8edf69c
Make Node SDK shutdown await in-flight delivery
KAVentures Sep 27, 2026
252ea2a
Test custom Python SDK shutdown admission semantics
KAVentures Sep 27, 2026
d5c7325
Test Node SDK waits for in-flight delivery on shutdown
KAVentures Sep 27, 2026
2652f9f
Export Python SDK observer stats
KAVentures Sep 27, 2026
793c4db
Keep custom harness setup paths privacy-minimal
KAVentures Sep 27, 2026
defc695
Bump OpenWorkGraph to v0.95.0
KAVentures Sep 27, 2026
d805be7
Align root package version to v0.95.0
KAVentures Sep 27, 2026
ce5f102
Preserve root packaging config while bumping v0.95.0
KAVentures Sep 27, 2026
30bc83f
Align Claude MCP bundle to v0.95.0
KAVentures Sep 27, 2026
ebb16ee
Align v0.95 release contract and SDK asset checks
KAVentures Sep 27, 2026
acbf339
Package custom agent SDKs in v0.95 releases
KAVentures Sep 27, 2026
e3dd4de
Document v0.95 custom harness support
KAVentures Sep 27, 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
45 changes: 44 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,39 @@ jobs:
path: dist/OpenWorkGraph-Claude.mcpb
if-no-files-found: error

sdk-package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Validate standalone custom-agent SDKs
run: |
python -m py_compile sdk/python/openworkgraph_agent.py
node --check sdk/typescript/index.mjs
- name: Stage standalone custom-agent SDK assets
run: |
mkdir -p dist
cp sdk/python/openworkgraph_agent.py dist/OpenWorkGraph-Agent-Python.py
cp sdk/typescript/index.mjs dist/OpenWorkGraph-Agent-Node.mjs
cp sdk/typescript/index.d.ts dist/OpenWorkGraph-Agent-Node.d.ts
- name: Upload custom-agent SDK artifact
uses: actions/upload-artifact@v4
with:
name: pkg-agent-sdk
path: |
dist/OpenWorkGraph-Agent-Python.py
dist/OpenWorkGraph-Agent-Node.mjs
dist/OpenWorkGraph-Agent-Node.d.ts
if-no-files-found: error

publish-release:
if: github.ref == 'refs/heads/main'
needs: [macos-package, windows-package, mcpb-package]
needs: [macos-package, windows-package, mcpb-package, sdk-package]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -87,6 +117,11 @@ jobs:
with:
name: OpenWorkGraph-Claude
path: release-assets
- name: Download custom-agent SDK assets
uses: actions/download-artifact@v4
with:
name: pkg-agent-sdk
path: release-assets
- name: Publish GitHub Release when VERSION is new
env:
GH_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -120,6 +155,11 @@ jobs:
### Agent setup control plane
The dashboard now separates giving an AI access to OpenWorkGraph context from observing an agent's own execution. It provides reviewable setup material for Claude Code lifecycle hooks, Codex trace export, OpenAI Agents tracing and generic OpenTelemetry/custom structural adapters. OpenWorkGraph does not silently edit third-party configuration files. An integration is shown as active only when telemetry actually observed by the local evidence store supports that status.

### Custom harnesses
Arbitrary self-built or third-party agent harnesses can now connect in either or both directions. Python and Node/TypeScript helpers, OTLP/HTTP JSON and raw structural HTTP can send privacy-safe execution telemetry through the dedicated write-only agent credential. Any MCP-capable harness can separately read only the OpenWorkGraph context the user has authorized. The setup flow keeps telemetry write permission and context/history read permission explicitly separate.

The standalone helpers do not accept or serialize prompt text, model responses, tool arguments/results, returned values, exception text or hidden reasoning. OpenWorkGraph observer failures remain fail-open for the agent. This release publishes **OpenWorkGraph-Agent-Python.py**, **OpenWorkGraph-Agent-Node.mjs** and **OpenWorkGraph-Agent-Node.d.ts** as standalone release assets.

### Compact MCP for new connections
New dashboard-generated connections, the Claude MCP bundle and the on-demand local HTTP bridge expose a compact MCP surface focused on current context, search, canonical evidence, repeated workflows, task context, prior-run feedback and agent execution inspection. Overlapping tools are consolidated so AI clients have fewer competing tool definitions.

Expand Down Expand Up @@ -158,6 +198,9 @@ jobs:
release-assets/OpenWorkGraph-Windows.zip \
release-assets/OpenWorkGraph-Windows.zip.sha256 \
release-assets/OpenWorkGraph-Claude.mcpb \
release-assets/OpenWorkGraph-Agent-Python.py \
release-assets/OpenWorkGraph-Agent-Node.mjs \
release-assets/OpenWorkGraph-Agent-Node.d.ts \
--target "$GITHUB_SHA" \
--title "OpenWorkGraph ${TAG}" \
--notes-file /tmp/openworkgraph-release-notes.md
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.94.0
0.95.0
90 changes: 90 additions & 0 deletions dashboard/custom_harness_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
(() => {
let cache=null;
const esc=value=>{const node=document.createElement('div');node.textContent=String(value??'');return node.innerHTML;};

function installStyle(){
if(document.querySelector('#owg-custom-harness-style'))return;
const style=document.createElement('style');style.id='owg-custom-harness-style';
style.textContent=`
.harness-card{border:1px solid var(--line);border-radius:14px;padding:15px;background:#f8faf8;display:flex;flex-direction:column;min-height:205px}
.harness-card h3{font-size:15px;margin:0 0 5px}.harness-card p{font-size:12.5px;color:var(--muted);line-height:1.45;margin:0 0 12px}.harness-card .actions{margin-top:auto}
.harness-badges{display:flex;gap:6px;flex-wrap:wrap;margin:4px 0 10px}.harness-badge{font-size:10.5px;border-radius:999px;padding:3px 7px;background:#eef3ee;color:#385143;font-weight:700}
.harness-methods{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0}.harness-methods button{min-height:32px;padding:5px 9px;font-size:12px}.harness-methods button.active{background:#2f7d55;color:#fff}
.harness-code{white-space:pre-wrap;overflow-wrap:anywhere;background:#f3f4f0;border:1px solid var(--line);border-radius:9px;padding:10px;font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;margin:7px 0}
.harness-two-way{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:12px 0}.harness-two-way>div{border:1px solid var(--line);border-radius:10px;padding:10px;font-size:12px}.harness-two-way strong{display:block;margin-bottom:3px}
@media(max-width:620px){.harness-two-way{grid-template-columns:1fr}}
`;
document.head.appendChild(style);
}

async function load(){
if(cache)return cache;
await window.__owgAuthReady;
const response=await fetch('/v1/custom-harness-setup',{cache:'no-store'});
if(!response.ok)throw new Error('custom harness setup unavailable');
cache=await response.json();return cache;
}

function injectCard(){
const section=document.querySelector('#agent-observation-setup');
if(!section||document.querySelector('#customHarnessCard'))return;
const grid=section.querySelector('.setup-grid');if(!grid)return;
const card=document.createElement('div');card.id='customHarnessCard';card.className='harness-card';
card.innerHTML=`<div class="setup-tag">Any framework</div><h3>Your own agent / harness</h3><div class="harness-badges"><span class="harness-badge">OpenClaw / Hermes-style</span><span class="harness-badge">Internal agents</span><span class="harness-badge">Any MCP client</span></div><p>Connect an arbitrary harness in either or both directions: send its structural execution to OpenWorkGraph and optionally let it read the OWG context you authorize.</p><div class="actions"><button type="button" id="customHarnessSetupButton">Connect your agent</button></div>`;
grid.appendChild(card);
card.querySelector('#customHarnessSetupButton').onclick=openSetup;
}

function code(text,id){return `<div class="harness-code" id="${esc(id)}">${esc(text||'')}</div><button class="secondary" type="button" data-harness-copy="${esc(id)}">Copy</button>`;}
function privacy(payload){
const p=payload.privacy||{};
return `<div class="setup-privacy"><div>${p.prompt_content?'⚠':'✓'} No prompt content</div><div>${p.model_response_content?'⚠':'✓'} No response content</div><div>${p.tool_arguments?'⚠':'✓'} No tool arguments</div><div>${p.tool_results?'⚠':'✓'} No tool results</div><div>${p.reasoning?'⚠':'✓'} No reasoning</div><div>${p.exception_text?'⚠':'✓'} No exception text</div></div>`;
}

function methodBody(payload,method){
const write=payload.write||{};
if(method==='python'){
const x=write.python||{};
return `<p><strong>Python SDK.</strong> A dependency-free helper with context managers for runs, models and tools. It is fail-open: OWG going down never stops the agent.</p><h3>Install the tiny SDK</h3>${code(x.install,'harnessPyInstall')}<h3 style="margin-top:14px">Give it the local write-only credential</h3>${code(x.environment,'harnessPyEnv')}<h3 style="margin-top:14px">Instrument the harness</h3>${code(x.example,'harnessPyExample')}`;
}
if(method==='typescript'){
const x=write.typescript||{};
return `<p><strong>TypeScript / Node.</strong> Dependency-free ESM with TypeScript declarations. Node 18+.</p><h3>Get the SDK files</h3>${code(x.download,'harnessTsDownload')}<h3 style="margin-top:14px">Give it the local write-only credential</h3>${code(x.environment,'harnessTsEnv')}<h3 style="margin-top:14px">Instrument the harness</h3>${code(x.example,'harnessTsExample')}`;
}
if(method==='otel'){
const x=write.otel||{};
const token=(write.raw_http||{}).authorization||'';
const env=`export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="${x.endpoint||''}"\nexport OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/json"\nexport OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=${token}"`;
return `<p><strong>OpenTelemetry.</strong> If the harness already emits portable GenAI spans, this is the least invasive option. OWG accepts OTLP/HTTP JSON and ignores unknown spans rather than guessing.</p>${code(env,'harnessOtelEnv')}`;
}
const x=write.raw_http||{};
const curl=`curl -X POST ${x.endpoint||''} \\\n -H 'Content-Type: application/json' \\\n -H 'Authorization: ${x.authorization||''}' \\\n --data '${String(x.example||'').replaceAll("'","'\\''")}'`;
return `<p><strong>Raw HTTP.</strong> For any language/runtime: POST the canonical structural envelope directly. The credential is write-only.</p>${code(curl,'harnessRawHttp')}<div class="note">The server validates the complete batch and rejects content-bearing fields such as prompts, responses, messages, reasoning, tool arguments and tool results.</div>`;
}

async function openSetup(){
try{
const payload=await load();
const body=`<p>Use either direction independently. Observing an agent does <strong>not</strong> let it read your work history, and giving it MCP context does <strong>not</strong> automatically record its execution.</p><div class="harness-two-way"><div><strong>Agent → OpenWorkGraph</strong>Structural run/model/tool/handoff/approval/error telemetry through a write-only credential.</div><div><strong>OpenWorkGraph → Agent</strong>Optional MCP context, still controlled by OWG's AI-access switch and saved-history lease.</div></div>${privacy(payload)}<h3>1. Observe this harness</h3><div class="harness-methods" id="harnessMethods"><button type="button" data-method="python" class="active">Python</button><button type="button" data-method="typescript">TypeScript / Node</button><button type="button" data-method="otel">OpenTelemetry</button><button type="button" data-method="raw">Raw HTTP</button></div><div id="harnessMethodBody">${methodBody(payload,'python')}</div><h3 style="margin-top:18px">2. Optional: let the harness read OWG context via MCP</h3><p class="muted">Paste this standard stdio MCP server entry into any MCP-capable harness. Context access remains OFF unless you enable OWG's AI-access master switch; historical reads additionally require your saved-history lease.</p>${code(JSON.stringify(payload.read?.config||{},null,2),'harnessMcpConfig')}<div class="note">The telemetry token above cannot read anything. MCP is a separate connection and permission path.</div>`;
window.openModal?.('Connect your agent','Custom harness · two-way connection',body);
bind(payload);
}catch(_){window.openModal?.('Custom harness setup unavailable','Connect','<p>Could not generate local setup material. Confirm OpenWorkGraph is running and reload the dashboard.</p>');}
}

function bind(payload){
document.querySelectorAll('[data-harness-copy]').forEach(button=>button.onclick=()=>copy(button.dataset.harnessCopy||'',button));
document.querySelectorAll('#harnessMethods [data-method]').forEach(button=>button.onclick=()=>{
document.querySelectorAll('#harnessMethods [data-method]').forEach(x=>x.classList.toggle('active',x===button));
const target=document.querySelector('#harnessMethodBody');if(target)target.innerHTML=methodBody(payload,button.dataset.method||'python');
document.querySelectorAll('[data-harness-copy]').forEach(copyButton=>copyButton.onclick=()=>copy(copyButton.dataset.harnessCopy||'',copyButton));
});
}

async function copy(id,button){
const text=document.querySelector(`#${CSS.escape(id)}`)?.textContent||'';
try{await navigator.clipboard.writeText(text);const before=button.textContent;button.textContent='Copied';setTimeout(()=>button.textContent=before,1100);}catch(_){window.prompt('Copy this:',text);}
}

function install(){installStyle();injectCard();setTimeout(injectCard,150);setTimeout(injectCard,800);}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',install);else install();
})();
38 changes: 38 additions & 0 deletions docs/CHANGELOG_V095.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# OpenWorkGraph v0.95.0

## Custom agents and harnesses

OpenWorkGraph now exposes a first-class, framework-neutral path for arbitrary agent runtimes. A self-built harness, an OpenClaw/Hermes-style setup, an internal company agent, or another framework can participate without OpenWorkGraph needing a named native adapter.

The connection is deliberately two-way and independent:

- **agent -> OpenWorkGraph:** privacy-safe structural execution telemetry through the existing write-only agent-ingest boundary;
- **OpenWorkGraph -> agent:** optional authorized context over the compact local MCP surface.

Giving a harness the telemetry token never gives it context/history read access. Giving a harness MCP context never silently enables observation of its execution.

## Standalone helpers

v0.95 adds:

- a dependency-free Python helper (`openworkgraph-agent`) with run/model/tool context managers;
- a dependency-free Node 18+/TypeScript helper with declarations;
- existing OTLP/HTTP JSON integration for harnesses that already emit portable GenAI traces;
- raw structural HTTP for any other language/runtime.

The GitHub release publishes the Python and Node helper files as standalone assets in addition to the normal macOS, Windows and Claude MCP packages.

## Privacy boundary

The custom helpers intentionally have no API for prompt text, model-response content, tool arguments/results, returned values, exception text or hidden reasoning. Tests verify that returned secrets and exception messages remain in the harness and never enter telemetry.

Observation failures remain fail-open for the agent: bounded background delivery can drop telemetry, but it does not enter the agent's control path.

## Reliability

- Python telemetry admission and shutdown share a synchronization boundary, preventing events from being accepted after the worker has closed.
- Node shutdown waits for an already in-flight telemetry request before returning.
- The local dashboard setup endpoint is authenticated and non-cacheable.
- The generated setup keeps write-only telemetry credentials separate from MCP/history permissions.

See [Custom agent harnesses](CUSTOM_HARNESSES.md) for the integration model and examples.
Loading
Loading