Skip to content

Commit 68b13f0

Browse files
authored
Oracle Waves 2+3: capability catch-up + UI v3 bundle (v2.48.0-v2.49.0) (#29)
* fix(oracle): close unauthenticated local-write kill chain with per-boot session cookie POST /api/apikey/generate|rotate|clear, /api/chat, /api/suggestions/approve and /api/config were reachable by any local process; rotate returned the fresh Discovery token, defeating the Bearer gates on /api/config and /api/discovery/*. New oracle/services/local_session.py issues a per-boot HttpOnly SameSite=Strict cookie on GET / to loopback clients only; a default-deny _local_write_guard requires session cookie OR Bearer on every mutating /api/* call outside /api/discovery/*. Session also un-breaks the dashboard Settings save (the UI never sent a Bearer token) and lets the dashboard reveal the Discovery key. Discovery stays Bearer-only. * perf(oracle): TTL-cache ProjectScanner.discover with copy-on-return discover() re-ran the hub fetch + per-project facts enrichment on every tool call (validate_project_path) and multiple times per graph request. Now cached for scanner_ttl_seconds (default 20s) under a lock; cache hits return deep copies (api_projects mutates the dicts), empty discoveries are never cached, and the dashboard Scan action forces a refresh. * refactor(oracle): C3Bridge adopts shared ProjectRuntimeCache with warm-on-build Deletes the hand-rolled 3-slot LRU (the shared cache's own docstring names it as the predecessor it was lifted from). C3Bridge now holds a PRIVATE ProjectRuntimeCache (size 8, C3_RUNTIME_CACHE_SIZE-tunable) so cross-project search over >3 projects stops thrashing, while keeping its atexit shutdown semantics. New on_build hook on ProjectRuntimeCache (default None, existing callers untouched) fires once per newly built runtime; the Oracle uses it to warm embedding_index + vector_store in a daemon thread, mirroring the MCP server's lifespan warm, so the first c3_search stops paying chromadb init on the request thread. Read-only enforcement and validate_project_path are unchanged. * feat(oracle): native Ollama tool calling in ChatEngine with legacy fallback Chat previously described tools in the system prompt and regex-parsed <tool_call> text blocks, patched with retry/trust heuristics. Now: - OllamaBridge: show()/supports_tools() capability probe (cached; None = unknown), set_tools_support() negative-cache poke, stream_chat(tools=) yielding structured ("tool_call", {name, arguments}) events. - ChatEngine: native tools array built from TOOL_SPECS (single source of truth; api_max_tier caps external callers only); one shared _drain_stream generator replaces three copies of the chunk-unpacking loop (main loop, visible-retry, delegate sub-agent). Three-way mode: tools-capable models run native (no stripper, no trust-answer heuristic, role:tool feeding, no finalize nudge); incapable models keep the legacy text protocol verbatim; unknown-capability models attempt native and demote mid-turn on HTTP 400, negative-caching only when the server names tools as the problem. Sub-agents pick their protocol from their own model. - SSE event vocabulary, payload shapes, and persisted round_messages roles unchanged — oracle.html needs no changes; history reloads re-inject <tool_result> user messages in both modes. - Hygiene: is_available() no longer reports a 5xx-failing server as up; LLM disk cache gains TTL (llm_cache_ttl_sec, default 86400) and a 512-entry bound evicting oldest by mtime. First-ever ChatEngine test coverage (18 tests) + bridge tests (14). * docs: CHANGELOG entry for v2.47.0 Oracle Wave 1; ruff import-order fix * feat(oracle): expose c3_project + c3_artifacts as read-tier Discovery/chat tools Deny-by-default allowlists: c3_project permits list/info/subprojects/search/ read/compress/status/memory/impact/edits/validate — register/unregister, sub_* mutations, edit/shell, and 'scan' (reveals unregistered .c3 projects, outside the discovered-project trust boundary) are excluded. The wrapper signature has no allow_write and no write-op params; the registry drops undeclared keys, so write verbs cannot reach handle_project from any transport. Every resolution passes resolve_project THEN validate_project_path (the resolver accepts any on-disk .c3 folder; Oracle re-checks membership). c3_artifacts blocks scan (mutates the manifest despite the handler's READ_ACTIONS listing) and restore; list/history/show/diff/status proxy through the validated runtime cache. MCP + OpenAPI pick both up from TOOL_SPECS automatically. Tests pin the allowlist enums, the allow_write drop, and _BLOCKED_MEMORY_ACTIONS == cli.tools.project._MEMORY_WRITE. * feat(oracle): sub-project awareness — scanner hierarchy, graph overlay, scoped cross tools The Oracle treated every project as flat, ignoring the v2.44 parent/child model. Now: ProjectScanner carries registry parent_path (previously dropped) and _enrich adds is_subproject / parent_path (child-config back-link as fallback; broken links degrade to top-level) / subproject_rel_paths / count — /api/projects and the list_projects chat tool surface hierarchy for free. FederatedGraph gains a serve-time parent_child overlay applied on BOTH fresh builds and cache hits (hierarchy lives in .c3/config.json, which the facts-mtime cache key never sees; the overlay is never baked into the cache file, and no fact-level edges are added — they would pollute similarity clustering). c3_search_cross / c3_edits_cross gain an optional scope param: '' = all, 'top' = top-level only, or a project name/path = that project plus its direct sub-projects (resolved + validated). * feat(oracle): scheduled activity digest via the review loop ActivityReporter was fully built but only reachable on demand. The review loop now emits a cross-project digest when due: config-gated (digest_enabled default FALSE — current behavior preserved; digest_interval_seconds daily; digest_narrate opt-in since narration costs a cloud LLM call), config read live each cycle so toggling needs no restart, last_digest_at pre-stamped in review_state.json so an overlapping run_now can't double-digest, digests persisted to ~/.c3/oracle/activity_digests/<date>.json + latest.json with retention pruning (digest_retention_days), optional one-line JSONL notify sink (digest_notify_file), and the whole path try/except-wrapped so a digest failure never kills the review cycle. New GET /api/activity/digest/latest; Activity tab shows a last-scheduled-digest banner; Settings gains the toggle and interval (round-trips through the existing /api/config). * feat(oracle): multi-backend delegate_task via c3_delegate (codex/gemini/claude/auto) Oracle agents ran only on Ollama while core C3's delegate layer already had hardened Codex/Gemini/Claude CLI backends. Agents now carry a per-agent backend (default 'ollama' = unchanged nested tool loop); CLI backends route through cli.tools.delegate.handle_delegate against _OracleDelegateRuntime — a read-only shim of the target project's runtime that forces codex/gemini memory bridges OFF (they write facts into the target), notifications None (no NotificationStore writes), and codex_default_sandbox='read-only', while passing everything else through. CLI backends need a concrete project (subprocess cwd + the TARGET's own delegate config; Oracle never force-enables a backend): explicit project_path arg → conversation's focused project (via thread-local conv state) → instructive error; never silently picked. delegate_task spec/defs gain optional project_path; agent modal gains a backend select; delegate progress notes stream to the UI through the existing agent event sink. * docs: CHANGELOG entry for v2.48.0 Oracle Wave 2; ruff import-order fix * 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 a25ebf9 commit 68b13f0

46 files changed

Lines changed: 6185 additions & 50 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,122 @@ 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+
54+
## [2.48.0] - Unreleased
55+
56+
### Oracle Wave 2: capability catch-up
57+
58+
#### New Discovery/chat tools
59+
60+
- **`c3_project`** (read tier) — cross-project operations by registered
61+
project NAME or path: registry listing, project info, sub-project tree, and
62+
read-only proxied ops (search/read/compress/status/memory/impact/edits/
63+
validate). Deny-by-default allowlist; the wrapper signature has no
64+
`allow_write` and no write-op params, and the registry drops undeclared
65+
keys, so write verbs cannot reach `handle_project` from any transport.
66+
`scan` is excluded (it reveals unregistered `.c3` projects, outside the
67+
Oracle's discovered-project trust boundary), and every resolution is
68+
re-validated against discovered projects (the resolver alone accepts any
69+
on-disk `.c3` folder).
70+
- **`c3_artifacts`** (read tier) — agent-config version history for one
71+
project: list/history/show/diff/status. `scan` (mutates the target's
72+
manifest despite the handler's READ_ACTIONS listing) and `restore` are
73+
blocked. Both tools appear on MCP + OpenAPI automatically via `TOOL_SPECS`.
74+
75+
#### Sub-project awareness (v2.44 parent/child model)
76+
77+
- `ProjectScanner` carries the registry's `parent_path` (previously dropped)
78+
and enriches every project with `is_subproject` / `parent_path` (child
79+
config back-link as fallback; broken links degrade to top-level) /
80+
`subproject_rel_paths` / `subproject_count``/api/projects` and the
81+
`list_projects` tool surface hierarchy for free.
82+
- `FederatedGraph` gains a **serve-time `parent_child` overlay** applied on
83+
both fresh builds and cache hits: hierarchy lives in `.c3/config.json`,
84+
which the facts-mtime cache key never sees, so it is recomputed per serve
85+
and never baked into the cache file. Project-level links only.
86+
- `c3_search_cross` / `c3_edits_cross` gain an optional **`scope`** param:
87+
`''` = all projects, `'top'` = top-level only, or a project name/path =
88+
that project plus its direct sub-projects.
89+
90+
#### Scheduled activity digest
91+
92+
- The review loop now emits the cross-project activity digest when due —
93+
config-gated (`digest_enabled` default **false**: current behavior
94+
preserved), live-read each cycle (no restart to toggle), pre-stamped
95+
`last_digest_at` (no double-digest from `run_now`), persisted to
96+
`~/.c3/oracle/activity_digests/<date>.json` + `latest.json` with retention
97+
pruning (`digest_retention_days`) and an optional one-line JSONL notify
98+
sink (`digest_notify_file`). `digest_narrate` stays opt-in (cloud LLM
99+
call). New `GET /api/activity/digest/latest`; the Activity tab shows a
100+
last-scheduled-digest banner and Settings gains the toggle + interval.
101+
102+
#### Multi-backend agents
103+
104+
- `delegate_task` agents gain a per-agent **backend**: `ollama` (default,
105+
unchanged nested tool loop) or `codex`/`gemini`/`claude`/`auto` routed
106+
through `cli.tools.delegate` against `_OracleDelegateRuntime` — a read-only
107+
shim of the target project's runtime that forces the codex/gemini memory
108+
bridges off, suppresses NotificationStore writes, and pins the codex
109+
sandbox to read-only. CLI backends require a concrete registered project
110+
(explicit `project_path` → conversation's focused project → instructive
111+
error; never silently picked) and honor the target project's own
112+
backend-enablement config. Agent modal gains a backend selector.
113+
114+
#### Tests
115+
116+
- 50 new tests: bridge wrapper contracts (`test_c3_bridge_project_artifacts.py`,
117+
incl. a pin that the Oracle's blocked-memory set equals
118+
`cli.tools.project._MEMORY_WRITE`), registry allow_write kill-switch pins,
119+
sub-project enrichment + scoping (`test_oracle_subproject_awareness.py`),
120+
hierarchy overlay on fresh AND cached graph builds, digest scheduling
121+
(`test_review_digest.py`), and CLI-backend delegate routing + shim contract.
122+
7123
## [2.47.0] - Unreleased
8124

9125
### Oracle Wave 1: security + core hardening/unification

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.

0 commit comments

Comments
 (0)