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
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ jobs:

OpenWorkGraph is local-first, open-source context infrastructure for how human and AI-agent work actually happens.

### First useful reconstruction
The local dashboard now has an evidence-driven first-value layer designed to become useful during the first real work session. It watches only the existing privacy-hardened local evidence surfaces and, once enough activity exists, offers a deterministic **Your last few minutes** reconstruction. This is a presentation layer over canonical evidence, not a new inference or capture pipeline.

The activation layer adds no sensor, OS/browser permission, AI permission, retention permission, Gateway sharing, MCP tool, screenshot capture, filesystem watcher, prompt/response capture or content telemetry. It never auto-enables AI access or saved history. Empty first-run placeholders are suppressed until their underlying features have useful data, while Evidence, History, Agents, Connect, Organization and Export remain available unchanged.

### Local-first remains the default
A normal OpenWorkGraph install captures to local SQLite, provides local dashboard/export/API/MCP access, and requires no OpenWorkGraph account or OpenWorkGraph-hosted evidence store. Capture continues locally if an optional customer-controlled Gateway or network is unavailable.

Expand All @@ -156,7 +161,7 @@ jobs:
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.
Arbitrary self-built or third-party agent harnesses can 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.

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.95.0
0.96.0
281 changes: 281 additions & 0 deletions dashboard/first_value_activation.js

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions docs/CHANGELOG_V096.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# OpenWorkGraph v0.96 — first useful reconstruction

v0.96 adds a thin first-run activation layer over the existing local evidence stack. It is intended to make OpenWorkGraph understandable during the first real work session without changing what the product captures or what an AI may access.

## What changes

- Overview shows a dismissible **See what OpenWorkGraph understands** card.
- Progress is evidence-driven rather than a countdown: observed events, work surfaces, transitions and agent runs.
- Once enough current-session evidence exists, **Your last few minutes** presents a deterministic reconstruction from existing privacy-hardened dashboard evidence and structural agent-run reports.
- If interaction-level evidence is sparse, already-derived observed surface transitions provide a factual fallback rather than inventing task intent.
- The next action is contextual: Connect AI only when the user chooses, optional browser-sensor setup when browser context is shallow, and optional native/OTel agent telemetry when an agent is only surface-observed.
- The old unfinished timeline placeholder and an empty repeated-workflows card are suppressed on first run until they have useful content. Their underlying DOM/data paths remain intact.

## What does not change

v0.96 adds no capture sensor, screenshot capture, filesystem watcher, prompt/response capture, clipboard-content capture, browser/OS permission, database schema, retention rule, AI permission, saved-history lease, Gateway behavior, agent-ingest permission, export format or MCP tool.

The first-value layer performs GET requests only against existing authenticated local endpoints. It cannot enable AI access, save history, synchronize evidence or mutate canonical evidence.

## Compatibility goal

Evidence, History, Agents, Connect, Organization and Export remain the established advanced surfaces. Dismissing the first-value card leaves the ordinary dashboard behavior intact. Existing installations and existing MCP configurations keep their prior semantics.
4 changes: 2 additions & 2 deletions mcpb/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"manifest_version": "0.3",
"name": "openworkgraph-local",
"display_name": "OpenWorkGraph",
"version": "0.95.0",
"version": "0.96.0",
"description": "Connect Claude Desktop to the compact local OpenWorkGraph context surface.",
"long_description": "Uses the OpenWorkGraph installation already running on this computer. v0.95 adds a framework-neutral custom-harness setup flow plus standalone Python and Node helpers for privacy-safe structural agent telemetry; arbitrary MCP-capable harnesses can separately read authorized OpenWorkGraph context. v0.94 added explicit local history retention, separate saved-history AI access, lightweight history navigation, and structural browser-agent lifecycle observation. Canonical workflow evidence remains primary; Context Pulse provides incremental factual updates. Retained history is separately user-controlled: list_history can navigate saved human and agent sessions only while a time-limited saved-history lease is active. The legacy 24-tool MCP entrypoint remains available for existing configurations while new connections use this compact surface. Prompts, model responses, tool arguments/results, typed text, clipboard contents, exception text, returned values, and hidden reasoning are not captured by the custom agent helpers.",
"long_description": "Uses the OpenWorkGraph installation already running on this computer. v0.96 adds an evidence-driven first-value dashboard layer that reconstructs the current session from existing privacy-hardened evidence without adding sensors, permissions, AI access, retention, or MCP capabilities. v0.95 added a framework-neutral custom-harness setup flow plus standalone Python and Node helpers for privacy-safe structural agent telemetry; arbitrary MCP-capable harnesses can separately read authorized OpenWorkGraph context. v0.94 added explicit local history retention, separate saved-history AI access, lightweight history navigation, and structural browser-agent lifecycle observation. Canonical workflow evidence remains primary; Context Pulse provides incremental factual updates. Retained history is separately user-controlled: list_history can navigate saved human and agent sessions only while a time-limited saved-history lease is active. The legacy 24-tool MCP entrypoint remains available for existing configurations while new connections use this compact surface. Prompts, model responses, tool arguments/results, typed text, clipboard contents, exception text, returned values, and hidden reasoning are not captured by the custom agent helpers.",
"author": {"name": "Koyar Afrasyab / Kinvectum"},
"repository": {"type": "git", "url": "https://github.com/KAVentures/openworkgraph"},
"server": {"type": "node", "entry_point": "server/index.js", "mcp_config": {"command": "node", "args": ["${__dirname}/server/index.js"], "env": {}}},
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "workflow-observer"
version = "0.95.0"
version = "0.96.0"
description = "Local-first work evidence, self-hosted organizational context gateway, REST API, and MCP access."
requires-python = ">=3.11"
license = {file = "LICENSE"}
Expand Down
2 changes: 1 addition & 1 deletion sdk/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "openworkgraph-agent"
version = "0.95.0"
version = "0.96.0"
description = "Dependency-free structural telemetry helper for custom OpenWorkGraph agent harnesses"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openworkgraph/agent",
"version": "0.95.0",
"version": "0.96.0",
"description": "Dependency-free structural telemetry helper for custom OpenWorkGraph agent harnesses",
"type": "module",
"exports": {
Expand Down
1 change: 1 addition & 0 deletions server/enterprise_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def main() -> None:
import server.browser_agent_projection # noqa: F401
import server.agent_dashboard_control_plane # noqa: F401
import server.custom_harness_control_plane # noqa: F401
import server.first_value_activation # noqa: F401
import server.org_join_routes as org_join_routes
import server.dashboard_privacy # noqa: F401

Expand Down
55 changes: 55 additions & 0 deletions server/first_value_activation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from __future__ import annotations

from fastapi import Request, Response
from fastapi.responses import HTMLResponse

from .main import ROOT
from .secure_app import app


SCRIPT_TAG = '<script src="/first-value-activation.js"></script>'


@app.get("/first-value-activation.js", include_in_schema=False)
def first_value_activation_javascript() -> Response:
"""Serve the additive first-value dashboard layer.

The script reads only existing authenticated local endpoints. It adds no
capture sensor, data store, AI permission, retention permission, or MCP
capability.
"""
path = ROOT / "dashboard" / "first_value_activation.js"
return Response(
path.read_text(encoding="utf-8"),
media_type="application/javascript",
headers={"Cache-Control": "no-store", "X-Content-Type-Options": "nosniff"},
)


@app.middleware("http")
async def inject_first_value_activation(request: Request, call_next):
response = await call_next(request)
if request.method.upper() != "GET" or request.url.path != "/" or response.status_code != 200:
return response
if "text/html" not in str(response.headers.get("content-type") or "").lower():
return response
try:
if hasattr(response, "body_iterator"):
chunks = [chunk async for chunk in response.body_iterator]
body = b"".join(
chunk if isinstance(chunk, bytes) else str(chunk).encode("utf-8")
for chunk in chunks
)
else:
body = bytes(getattr(response, "body", b""))
text = body.decode("utf-8")
except Exception:
return response
if SCRIPT_TAG not in text:
text = text.replace("</body>", SCRIPT_TAG + "\n</body>")
headers = dict(response.headers)
headers.pop("content-length", None)
return HTMLResponse(text, status_code=response.status_code, headers=headers)


__all__ = ["first_value_activation_javascript"]
34 changes: 34 additions & 0 deletions tests/js/first_value_activation.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';

const source = fs.readFileSync(path.resolve('dashboard/first_value_activation.js'), 'utf8');

test('first-value activation JavaScript parses standalone', () => {
assert.doesNotThrow(() => new Function(source));
});

test('first-value activation is read-only and evidence driven', () => {
assert.match(source, /\/v1\/summary\?scope=current&limit=500/);
assert.match(source, /\/v1\/agent-execution-traces\?/);
assert.match(source, /\/v1\/ai-access/);
assert.match(source, /state\.ready/);
assert.match(source, /Observed evidence only/);
assert.doesNotMatch(source, /method:\s*['"](?:POST|PUT|PATCH|DELETE)['"]/i);
});

test('first-value activation never captures content or hidden reasoning', () => {
for (const forbidden of [
'getDisplayMedia(', 'getUserMedia(', 'clipboard.read(', 'clipboard.readText(',
'/agent-ingest/', '/v1/events', '/v1/context-events'
]) assert.equal(source.includes(forbidden), false, forbidden);
assert.match(source, /does not infer intent or read hidden reasoning/);
});

test('first-value guide is dismissible and does not trap advanced navigation', () => {
assert.match(source, /owg_first_value_dismissed_v1/);
assert.match(source, /window\.activateTab\?\.\('evidence'\)/);
assert.match(source, /window\.activateTab\?\.\('connect'\)/);
assert.match(source, /window\.activateTab\?\.\('organization'\)/);
});
86 changes: 86 additions & 0 deletions tests/test_first_value_activation_v096.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from __future__ import annotations

from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
JS = ROOT / "dashboard" / "first_value_activation.js"
SERVER = ROOT / "server" / "first_value_activation.py"
RUNNER = ROOT / "server" / "enterprise_runner.py"


def test_first_value_layer_is_additive_and_loaded_by_desktop_runner():
server = SERVER.read_text(encoding="utf-8")
runner = RUNNER.read_text(encoding="utf-8")
assert 'from .secure_app import app' in server
assert '/first-value-activation.js' in server
assert 'server.first_value_activation' in runner
assert 'SCRIPT_TAG + "\\n</body>"' in server


def test_activation_layer_reads_existing_surfaces_only():
js = JS.read_text(encoding="utf-8")
for endpoint in (
"/v1/summary?scope=current&limit=500",
"/v1/agent-execution-traces?limit=10&evidence_limit=3000&max_events_per_execution=20",
"/v1/ai-access",
):
assert endpoint in js
# First-value activation must not become a second control plane. All writes
# remain behind the established History/Connections/Organization surfaces.
for mutation in ("method:'POST'", 'method:"POST"', "method:'PUT'", 'method:"PUT"', "method:'DELETE'", 'method:"DELETE"'):
assert mutation not in js
assert "fetch(url,{cache:'no-store'})" in js


def test_activation_layer_adds_no_capture_or_content_sensor():
js = JS.read_text(encoding="utf-8").lower()
forbidden_apis = (
"getdisplaymedia(",
"getusermedia(",
"filesystemobserver",
"clipboard.read",
"clipboard.readtext",
"mutationobserver(",
)
for marker in forbidden_apis:
assert marker not in js
# The layer may explain these privacy boundaries in UI copy, but it must not
# define content-bearing telemetry fields or write them to a new endpoint.
assert "/agent-ingest/" not in js
assert "/v1/events" not in js
assert "/v1/context-events" not in js


def test_activation_never_auto_enables_ai_or_retention():
js = JS.read_text(encoding="utf-8")
assert "/v1/history-policy" not in js
assert "/v1/history/ai-access" not in js
assert "set_ai_access" not in js
assert "Connect AI" in js
assert "Connecting an AI is optional and does not change capture or retention" in js


def test_existing_mcp_and_browser_permissions_are_untouched_by_activation_layer():
# These files are read, not modified, by this feature. The assertions make
# the intended v0.96 boundary explicit so a future activation edit cannot
# quietly grow browser privileges or replace MCP with an onboarding API.
manifest = (ROOT / "browser_extension" / "manifest.json").read_text(encoding="utf-8")
assert '"permissions": ["tabs", "webNavigation", "storage", "alarms"]' in manifest
assert "first_value_activation" not in manifest
compact = (ROOT / "mcp_server" / "compact_stdio.py").read_text(encoding="utf-8")
legacy = (ROOT / "mcp_server" / "secure_stdio.py").read_text(encoding="utf-8")
assert "first_value_activation" not in compact
assert "first_value_activation" not in legacy


def test_stale_first_run_placeholders_are_hidden_without_deleting_their_dom():
js = JS.read_text(encoding="utf-8")
assert "Timeline lanes will activate in the stacked capture/timeline PR" in js
assert "card.style.display='none'" in js
assert "repeated_task_pattern_count" in js
# The base dashboard remains the source of the advanced interfaces; this
# layer only changes first-run presentation at runtime.
html = (ROOT / "dashboard" / "index.html").read_text(encoding="utf-8")
for tab in ("overview", "evidence", "connect", "organization", "export"):
assert f'data-tab="{tab}"' in html
3 changes: 2 additions & 1 deletion tests/test_release_version_v087.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


ROOT = Path(__file__).resolve().parents[1]
EXPECTED_VERSION = "0.95.0"
EXPECTED_VERSION = "0.96.0"


def test_release_version_sources_are_aligned():
Expand Down Expand Up @@ -40,6 +40,7 @@ def test_release_notes_are_current_and_version_driven():
assert "Agent setup control plane" in workflow
assert "telemetry actually observed" in workflow
assert "Custom harnesses" in workflow
assert "First useful reconstruction" in workflow
assert "pkg-agent-sdk" in workflow
assert "OpenWorkGraph-Agent-Python.py" in workflow
assert "OpenWorkGraph-Agent-Node.mjs" in workflow
Expand Down
Loading