Skip to content

Commit 7f5cfdd

Browse files
authored
Oracle Wave 3: UI v3 concat bundle + c3 oracle serve + docs refresh (v2.49.0) (#28)
* feat(oracle): UI v3 — split the 4,181-line oracle.html monolith into a concat bundle oracle.html was the pre-refactor hub.html pattern: one file, ~780 lines of CSS + ~400 lines of markup + a 2,967-line inline script. Following the hub v2 concat architecture: new oracle_ui.html shell (CSS + markup + __C3_ORACLE_SCRIPTS__ token) + 18 oracle/ui/ modules split at section banners (core, busy, theme_tabs, crossgraph, header, projects, insights, activity, suggestions, settings, agents, chat/{markdown,conversations, stream_renderer,toolbar,input,send}, app.js LAST — the init IIFE). _ORACLE_JS_FILES + _build_oracle_html() concatenate server-side with per-file markers; / serves the cached bundle, /legacy serves the frozen monolith for one release (both issue the dashboard session cookie). Extraction is VERBATIM: a splitter script cut exact line ranges with a tiling assertion (no gaps/overlaps), and a multiset diff proved the bundle differs from the original script by only the intentional ORACLE_BUILD_TIME bump. Whole-bundle esbuild parse clean. Deliberate deviation from the Wave-3 plan: no React/babel runtime — the logic is 100%% vanilla imperative and the transferable hub pattern is the file structure + build pipeline, not the framework; wrapping unowned vanilla code in React would add a CDN/transpile failure surface for zero owned UI. Also: pyproject package-data globs for oracle/ui + oracle/ui/chat (the '*' top-level-only gotcha), and a new 'c3 oracle serve|start' subcommand (lazy import, mirrors cmd_hub) so the server no longer requires the python entry point. New tests/test_oracle_ui_bundle.py; wheel inspection confirms all 18 modules + shell + legacy ship. * docs(oracle): oracle-guide caught up a full product generation; CHANGELOG v2.49.0 The guide documented the original memory/insight Oracle but none of what it became: architecture.md gains Chat subsystem (tool loop, native-vs-text protocol, full SSE event vocabulary), C3Bridge, Federated graph, and Security model sections, all 17 services listed, stale line counts fixed, Web UI section rewritten for the 8-tab concat bundle. api-reference.md adds the /api/apikey, /api/chat, /api/graph/federated, /api/insights/cross and /api/activity/digest/latest families plus the v2.47.0 write-gate auth note and fixed /api/health payload. configuration.md now covers all 30 DEFAULTS keys incl. the agents roster shape with the backend field. README leads with c3 oracle serve. discovery-api.md lists c3_project/c3_artifacts, the scope param, and delegate_task's project_path. changelog.md gains v1.3.0 (C3 v2.47.0-v2.48.0), v1.4.0 (C3 v2.49.0), and an honest 'previously undocumented' block for the v2.32-v2.38 era features. AGENTS.md tree updated; root CHANGELOG gains the v2.49.0 Wave 3 entry.
1 parent 40d4aff commit 7f5cfdd

32 files changed

Lines changed: 4802 additions & 27 deletions

AGENTS.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,11 @@ claude-companion - v2/
133133
__init__.py
134134
config.py
135135
mcp_oracle.py
136-
oracle.html
136+
oracle.html (legacy UI, served at /legacy for one release)
137+
oracle_ui.html (UI shell; JS concatenated from ui/ at serve time)
137138
oracle_server.py
138-
services/ (16 files)
139+
services/ (18 files)
140+
ui/ (12 files + chat/ 6 files)
139141
oracle-guide/
140142
README.md
141143
api-reference.md

CHANGELOG.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,53 @@ All notable changes to Code Context Control (C3) are documented here.
44
The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.49.0] - Unreleased
8+
9+
### Oracle Wave 3: UI v3 concat bundle + `c3 oracle serve` + docs refresh
10+
11+
#### UI architecture
12+
13+
- **The 4,181-line `oracle.html` monolith is gone.** The dashboard now
14+
follows the hub v2 concat architecture: an `oracle_ui.html` shell (CSS +
15+
markup + `__C3_ORACLE_SCRIPTS__` token) plus **18 `oracle/ui/` modules**
16+
split at section boundaries (core, busy, theme_tabs, crossgraph, header,
17+
projects, insights, activity, suggestions, settings, agents,
18+
`chat/{markdown,conversations,stream_renderer,toolbar,input,send}`, and
19+
`app.js` — the init IIFE — last), concatenated server-side by
20+
`_build_oracle_html()` with per-file markers. `GET /` serves the cached
21+
bundle; **`GET /legacy` serves the frozen monolith for one release**, then
22+
it will be removed. Extraction was verbatim (proven by a line-multiset
23+
diff: the only delta is the `ORACLE_BUILD_TIME` bump) — zero behavior
24+
change by construction.
25+
- Deliberate deviation from the original Wave-3 design: **no React/babel
26+
runtime**. The Oracle UI logic is entirely vanilla imperative JS; the
27+
transferable part of the hub pattern is the module structure + build
28+
pipeline, and wrapping unowned vanilla code in a framework would have
29+
added a CDN/transpile failure surface for zero owned UI.
30+
31+
#### CLI
32+
33+
- **`c3 oracle serve`** (alias `start`, `--port`, `--no-browser`) launches
34+
the Oracle dashboard — no more `python oracle/oracle_server.py` required
35+
(it still works). Lazy import keeps bare `c3` startup fast.
36+
37+
#### Packaging
38+
39+
- `pyproject.toml` package-data globs for `oracle/ui/*.js` +
40+
`oracle/ui/chat/*.js` (the `"*"` globs only match a package's top level);
41+
wheel inspection confirms all 18 modules + shell + legacy ship.
42+
43+
#### Docs
44+
45+
- `oracle-guide/` caught up a full product generation: chat subsystem + SSE
46+
event protocol, C3Bridge, federated graph, security model (session cookie
47+
+ write gate), the 8-tab bundle UI, all missing config keys (incl. Wave
48+
1–2 additions), missing endpoint families (`/api/apikey/*`, `/api/chat/*`,
49+
`/api/graph/federated/*`, `/api/activity/digest/latest`), Discovery tool
50+
list (c3_project/c3_artifacts/scope), and changelog entries v1.3.0 (Waves
51+
1–2) / v1.4.0 (Wave 3) with a catch-up block for the previously
52+
undocumented v2.32–v2.38 era features.
53+
754
## [2.48.0] - Unreleased
855

956
### Oracle Wave 2: capability catch-up

cli/c3.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5632,10 +5632,18 @@ def _bb_cmd_set_default(args, project_path: str) -> None:
56325632

56335633

56345634
def cmd_oracle(args):
5635-
"""Oracle Discovery API key + connection management."""
5635+
"""Oracle dashboard server + Discovery API key management."""
56365636
sub = getattr(args, "oracle_cmd", None)
5637+
if sub in ("serve", "start"):
5638+
# Lazy import: run_oracle builds all Oracle services (matches
5639+
# cmd_hub's deferred-import style so bare `c3` stays fast).
5640+
from oracle.oracle_server import run_oracle
5641+
run_oracle(port=getattr(args, "port", None),
5642+
open_browser=not getattr(args, "no_browser", False))
5643+
return
56375644
if sub != "api":
5638-
print("Usage: c3 oracle api {info,key,rotate,clear}")
5645+
print("Usage: c3 oracle {serve,api} — serve: launch the dashboard; "
5646+
"api {info,key,rotate,clear}: manage the Discovery key")
56395647
return
56405648

56415649
from oracle.config import load_config

cli/commands/parser.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,18 @@ def build_parser(version: str, parse_cli_ide_arg):
357357
# ── Oracle Discovery API (v2.32.0) ──────────────────────────────────
358358
p_oracle = subparsers.add_parser(
359359
"oracle",
360-
help="Oracle Discovery API key + connection management",
360+
help="Oracle dashboard server + Discovery API key management",
361361
)
362362
or_subs = p_oracle.add_subparsers(dest="oracle_cmd")
363+
or_serve = or_subs.add_parser(
364+
"serve",
365+
aliases=["start"],
366+
help="Launch the Oracle dashboard server (REST + MCP discovery endpoints)",
367+
)
368+
or_serve.add_argument("--port", type=int, default=None,
369+
help="Server port (default: config 'port', 3331)")
370+
or_serve.add_argument("--no-browser", action="store_true",
371+
help="Don't open the browser")
363372
or_api = or_subs.add_parser(
364373
"api",
365374
help="Show connection info / manage the Discovery API key",

oracle-guide/README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ Oracle is an independent AI-powered memory management agent for the C3 ecosystem
88
# Set your Ollama cloud API key
99
export OLLAMA_API_KEY=your-key-here
1010

11-
# Launch Oracle from the project root
12-
python oracle/oracle_server.py
11+
# Launch Oracle via the C3 CLI (v2.49.0+; alias: c3 oracle start)
12+
c3 oracle serve
1313

1414
# Or with options
15-
python oracle/oracle_server.py --port 3332 --no-browser
15+
c3 oracle serve --port 3333 --no-browser
16+
17+
# From a source checkout, the direct entry point still works
18+
python oracle/oracle_server.py [--port N] [--no-browser]
1619
```
1720

1821
Open `http://localhost:3331` in your browser.
@@ -23,6 +26,10 @@ Open `http://localhost:3331` in your browser.
2326
- **Ollama cloud**: Uses `https://ollama.com` with Bearer token auth by default. Can also target a local Ollama instance.
2427
- **Hub-aware**: Discovers projects through the C3 hub API or falls back to reading `~/.c3/projects.json` directly.
2528
- **Read + suggest-write**: Oracle reads project memory freely. Writes to project `.c3/facts/` require explicit user approval in the UI.
29+
- **Interactive chat**: a Chat tab (the default view) streams conversations with Oracle over SSE, with a tool-calling loop over the same tool registry the Discovery API exposes — native Ollama tool calling where the model supports it, text-protocol fallback otherwise.
30+
- **Team / Agents**: a configurable roster of specialist agents (Architect, Code Explorer, Memory Analyst by default) that chat can delegate sub-tasks to via `delegate_task`. Agents run on Ollama or, per-agent, on a CLI backend (codex/gemini/claude/auto) in read-only mode.
31+
- **Federated graph**: a Cross-Graph tab visualizes one memory graph across all projects, with embedding/TF-IDF similarity edges linking related facts between projects.
32+
- **Scheduled digest**: opt-in (`digest_enabled`) daily cross-project activity digest written by the background review loop and served at `/api/activity/digest/latest`.
2633
- **Discovery API (v2.32.0)**: External LLMs (Claude Code/Desktop or any function-calling model) can use Oracle's tools over MCP (`:3332/mcp`) and OpenAPI REST (`/api/discovery`) — Bearer-auth'd, loopback-bound, read + safe-action tiers only. See [Discovery API](discovery-api.md).
2734
- **Cross-project memory**: Maintains a global insight store (`~/.c3/oracle/cross_memory.json`) linking patterns, risks, and opportunities across projects.
2835
- **Background review**: A daemon thread periodically scans projects for changes, runs health checks, and generates consolidation suggestions.

oracle-guide/api-reference.md

Lines changed: 180 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Base URL: `http://localhost:3331` (configurable via `port` in config)
44

55
All endpoints return JSON. POST endpoints accept JSON bodies with `Content-Type: application/json`.
66

7+
> **Auth (v2.47.0)**: ALL mutating (POST/PUT/DELETE) endpoints outside `/api/discovery/*`
8+
> require either the dashboard session cookie (a per-boot `HttpOnly` cookie issued on
9+
> `GET /` to loopback browsers) or the Discovery Bearer token
10+
> (`Authorization: Bearer <token>`). Unauthenticated mutations get `401`. `GET` reads
11+
> are open to allowed local origins; `/api/discovery/*` is Bearer-only (see below).
12+
713
---
814

915
## Health & Config
@@ -17,7 +23,9 @@ Returns Oracle status and Ollama cloud availability.
1723
"status": "ok",
1824
"service": "c3-oracle",
1925
"model": "gemma4:31b-cloud",
20-
"ollama_available": true
26+
"ollama_available": true,
27+
"model_verified": true,
28+
"hub_available": true
2129
}
2230
```
2331

@@ -227,6 +235,55 @@ Dismiss an insight (marks `dismissed: true`, excluded from future listings).
227235
{ "dismissed": true, "id": "ins_a1b2c3d4e5f6" }
228236
```
229237

238+
### `POST /api/insights/cross`
239+
240+
On-demand cross-project insight generation from the federated graph. Requires at least
241+
2 projects with facts (defaults to all discovered projects with facts; override with
242+
`projects`).
243+
244+
```json
245+
// Request (optional body)
246+
{ "projects": ["/path/a", "/path/b"] }
247+
248+
// Response
249+
{ "generated": 3, "insights": [ ... ], "graph_stats": { ... } }
250+
```
251+
252+
---
253+
254+
## Federated Graph
255+
256+
Cross-project memory graph: per-project fact graphs merged, plus `cross_similar`
257+
edges between facts of different projects (embeddings with TF-IDF fallback). Builds
258+
are cached (`federated_graph_ttl_sec` + facts mtimes); the project hierarchy overlay
259+
is recomputed per serve.
260+
261+
### `GET /api/graph/federated`
262+
263+
**Query params:** `projects` (comma-separated paths; default = all discovered projects
264+
with facts), `min_sim` (similarity threshold override), `top_k` (neighbors per fact),
265+
`force=1` (bypass cache).
266+
267+
Returns `nodes`, `edges`, `projects` (each with `parent_slug`), `stats` (including
268+
`parent_child`), and `hierarchy` (parent/child project links).
269+
270+
### `GET /api/graph/federated/node/<id>`
271+
272+
One node's fact plus its cross-project neighbors.
273+
274+
```json
275+
{ "node": { ... }, "neighbors": [ { "id": "...", "type": "cross_similar",
276+
"scope": "cross", "weight": 0.82, "label": "...", "project": "..." } ] }
277+
```
278+
279+
### `POST /api/graph/federated/rebuild`
280+
281+
Invalidate the cache and force a rebuild. Optional body `{ "projects": [ ... ] }`.
282+
283+
### `GET /api/graph/federated/stats`
284+
285+
Graph stats + project list only (no nodes/edges).
286+
230287
---
231288

232289
## Activity
@@ -261,6 +318,19 @@ summary — best-effort, omitted on failure).
261318
> Also available as the discovery tool **`activity_report`** (identical payload) for
262319
> external LLMs over MCP, OpenAPI, and `POST /api/discovery/call`.
263320
321+
### `GET /api/activity/digest/latest`
322+
323+
Most recent **scheduled** digest, written by the review loop when `digest_enabled` is
324+
set (serves `~/.c3/oracle/activity_digests/latest.json`). On-demand digests from
325+
`/api/activity/digest` are not persisted here.
326+
327+
```json
328+
{ "generated_at": "2026-07-02T00:00:12+00:00", "digest": { ... } }
329+
330+
// No scheduled digest yet
331+
{ "digest": null, "generated_at": null }
332+
```
333+
264334
---
265335

266336
## Suggestions
@@ -368,6 +438,108 @@ Test generation with the current model. Sends a simple prompt and returns the re
368438

369439
---
370440

441+
## Chat
442+
443+
Interactive chat with Oracle (the dashboard's Chat tab). Conversations are persisted
444+
in `~/.c3/oracle/conversations/`.
445+
446+
### `POST /api/chat`
447+
448+
Streaming chat over SSE (`text/event-stream`). Each event is a JSON object on a
449+
`data:` line with a `type` field; the stream ends with a literal `data: [DONE]`.
450+
451+
```json
452+
// Request
453+
{ "message": "what changed across my projects today?", "conversation_id": "c_..." }
454+
```
455+
456+
**Event vocabulary** (documented once here; the same shapes appear in the agent
457+
sub-stream):
458+
459+
| Type | Meaning |
460+
|------|---------|
461+
| `meta` | First event: `conv_id`, `model`, conversation `state` |
462+
| `status` | Progress updates (`message` + `detail`) |
463+
| `thinking` | Streamed model reasoning chunks |
464+
| `text` | Streamed visible answer chunks |
465+
| `tool_call` | Oracle invoking a tool (`name`, `args`, `tool_id`) |
466+
| `tool_result` | Tool output (`tool_id`, result) |
467+
| `agent_start` / `agent_round` / `agent_thinking` / `agent_text` / `agent_tool_call` / `agent_tool_result` / `agent_done` | Sub-stream while `delegate_task` runs a roster agent |
468+
| `done` | Turn complete (timing/stats) |
469+
| `error` | Terminal error for the turn |
470+
471+
Tool calling is native Ollama tool calling when the model supports it (probed via
472+
`/api/show`), with automatic mid-turn fallback to the legacy `<tool_call>` text
473+
protocol if the model rejects native tools.
474+
475+
### `GET /api/chat/conversations?limit=50`
476+
477+
List conversations (most recent first).
478+
479+
### `POST /api/chat/conversations`
480+
481+
Create a conversation. Body `{ "title": "..." }` (optional). Returns `201` with `{ "id": "c_..." }`.
482+
483+
### `GET /api/chat/conversations/<id>`
484+
485+
Full message history: `{ "conversation_id": "...", "messages": [ ... ] }`.
486+
487+
### `DELETE /api/chat/conversations/<id>`
488+
489+
Delete a conversation. Returns `{ "ok": true }`.
490+
491+
### `PUT /api/chat/conversations/<id>/title`
492+
493+
Rename. Body `{ "title": "..." }`.
494+
495+
### `GET /api/chat/conversations/<id>/state`
496+
497+
Per-conversation state (focused projects, model, depth): `{ "state": { ... } }`.
498+
499+
### `GET /api/chat/commands`
500+
501+
Slash-command registry for frontend autocomplete: `{ "commands": [ ... ] }`.
502+
503+
### `POST /api/chat/command`
504+
505+
Execute a slash command (e.g. `/project`, `/model`, `/depth`, `/team`).
506+
507+
```json
508+
// Request
509+
{ "conversation_id": "c_...", "command": "/depth deep" }
510+
```
511+
512+
---
513+
514+
## Discovery API key (`/api/apikey`)
515+
516+
Dashboard-facing management of the Discovery Bearer token (stored in the OS keyring).
517+
The POST mutations are covered by the v2.47.0 write gate: they require the session
518+
cookie or the Bearer token.
519+
520+
### `GET /api/apikey`
521+
522+
Token status + connection info (`exists`, `masked`, `mcp_url`, `rest_base`,
523+
`openapi_url`, a ready-to-paste `.mcp.json` `snippet`, and the `api_enabled` /
524+
`api_require_auth` / `mcp_enabled` flags). The **unmasked** `key` is included only
525+
when the caller presents a valid Bearer token or the dashboard session cookie;
526+
otherwise `key` is empty and the snippet carries a `<token>` placeholder.
527+
528+
### `POST /api/apikey/generate`
529+
530+
Create a token if none exists. Reveals the key in the response (explicit local
531+
creation action, shown once for copy).
532+
533+
### `POST /api/apikey/rotate`
534+
535+
Replace the token with a fresh one (revealed once for copy).
536+
537+
### `POST /api/apikey/clear`
538+
539+
Delete the stored token.
540+
541+
---
542+
371543
## Discovery API (external LLM tool surface)
372544

373545
> Added in v2.32.0. Lets Claude or any function-calling LLM use C3's cross-project
@@ -431,11 +603,14 @@ Connection details for the MCP transport.
431603
### Capability tiers
432604

433605
- **read** — discovery only: `list_projects`, `search_facts`, `query_memory`,
434-
`project_health`, `analyze_project`, `cross_insights`, `read_graph`, `c3_search`,
435-
`c3_search_cross`, `c3_read`, `c3_compress`, `c3_validate`, `c3_status`,
436-
`c3_memory_query`, `c3_edits`, `c3_edits_cross`.
606+
`project_health`, `analyze_project`, `cross_insights`, `read_graph`,
607+
`activity_report`, `c3_search`, `c3_search_cross`, `c3_read`, `c3_compress`,
608+
`c3_validate`, `c3_status`, `c3_memory_query`, `c3_edits`, `c3_edits_cross`,
609+
`c3_project`, `c3_artifacts` (the latter two are read-only by construction:
610+
write verbs are blocked).
437611
- **action** — adds safe, non-code-edit writes: `suggest_action` (creates a
438-
*pending* suggestion for human approval) and `delegate_task`.
612+
*pending* suggestion for human approval) and `delegate_task` (optional
613+
`project_path`, required for CLI-backed agents).
439614
- Code-editing tools are **never** exposed. Cap the tier via `api_max_tier`
440615
(`read` | `action`) in `~/.c3/oracle/config.json`.
441616

0 commit comments

Comments
 (0)