Add sealed Epiphany telemetry artifacts

This commit is contained in:
Meta Vens 2026-04-30 03:41:33 +01:00
commit 8a16cadb70
13 changed files with 416 additions and 6 deletions

View file

@ -64,3 +64,8 @@ Raw transcripts, stderr logs, full turn logs, direct worker messages, and
use projected status, finding summaries, state patches, coordinator
recommendations, and manifests by default; open sealed artifacts only for
explicit debugging.
For more detail without reading the worker's direct stream, use the generated
`agent-function-telemetry.json` files. They summarize API/function-call shape,
job/status/path counts, and visible tool names while sealing text and raw
results.

View file

@ -37,6 +37,7 @@ The spine exists in twenty-five live paths:
- role specialist launch/read-back/acceptance: app-server declares experimental `thread/epiphany/roleLaunch`, read-only `thread/epiphany/roleResult`, and write-gated `thread/epiphany/roleAccept`. `roleLaunch` accepts only fixed modeling/checkpoint and verification/review role ids, builds bounded specialist payloads from authoritative Epiphany state, routes them through the existing `epiphany_launch_job` job-control seam, and emits the normal `jobLaunch` state update for `jobBindings` only. `roleResult` resolves the fixed or requested binding through the current `agent_jobs` backend and projects structured output as a reviewable finding with verdict, summary, next safe move, inspected files, frontier/evidence ids, optional modeling `statePatch`, raw result, and errors without mutation, promotion, scheduling, or hidden continuation. `roleAccept` currently accepts only completed modeling/checkpoint findings with a valid modeling-safe `statePatch`, applies it through the existing Epiphany state validator, appends audit observation/evidence, and emits `stateUpdated` with source `roleAccept`. Implementation stays owned by the main coding agent, verification output remains review-only, and reorientation stays on the CRRC-specific `reorientLaunch` / `reorientResult` / `reorientAccept` path.
- MVP operator view: `tools/epiphany_mvp_status.py` launches app-server over stdio, starts or reads one thread, gathers scene, pressure, reorient, jobs, roles, modeling/verification role results, reorient result, and CRRC responses, and renders them as text or JSON so the loop can be dogfooded without reading Rust.
- MVP dogfood runner: `tools/epiphany_mvp_dogfood.py` launches app-server over stdio, drives a bounded state/role/CRRC/reorientation loop, and writes operator-safe JSON/text artifacts plus sealed transcript diagnostics under `.epiphany-dogfood/mvp-loop` so a human can audit the run through projections and compare it against a vanilla Codex reference.
- Agent function/API telemetry: `tools/epiphany_agent_telemetry.py` mechanically parses sealed AppServerClient JSONL transcripts into operator-safe telemetry with method counts, request shape, job/status/path fields, and visible function/tool names while sealing text, turn items, raw results, and direct agent messages.
- reorientation launch: app-server now declares experimental `thread/epiphany/reorientLaunch`, requires a loaded thread plus durable investigation checkpoint, consumes the live reorientation verdict, and launches one fixed `reorient-worker` binding through the existing job-control seam with explicit resume-versus-regather scope, checkpoint-derived payload, and structured-output expectations instead of pretending CRRC suddenly grew a scheduler in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:370), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4575), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4519).
- reorientation result read-back: app-server now declares experimental read-only `thread/epiphany/reorientResult`, defaults to the fixed `reorient-worker` binding, resolves that binding through the current `agent_jobs` backend when available, reads the single backend item result, and projects a reviewable finding with mode, summary, next safe move, checkpoint-validity flag, inspected files, frontier ids, evidence ids, raw result, and any job/item errors without mutating state, promoting evidence, or scheduling follow-up work in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:374), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4610), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4684).
- reorientation finding acceptance: app-server now declares experimental `thread/epiphany/reorientAccept`, requires a loaded thread and a completed `reorient-worker` backend result, appends accepted observation/evidence records, optionally banks the finding into scratch and the durable investigation checkpoint, persists through the normal typed update path, and emits `thread/epiphany/stateUpdated` with source `reorientAccept` without accepting pending work, auto-promoting arbitrary worker output, launching follow-up work, or silently continuing implementation in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:378), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4635), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4885).

View file

@ -41,6 +41,7 @@ Do not trust this file for the exact live HEAD. Always check git.
- The prompt doctrine pass is landed. Shared Epiphany prompts now carry distilled memory/evidence discipline, and specialist prompt text lives in `vendor/codex/codex-rs/app-server/src/prompts/epiphany_specialists.toml`: modeling protects the body, verification protects the soul, reorientation protects life, and coordinator remains the read-only Self.
- The Aetheria dogfood run has a contamination scar. The supervising Codex session directly edited and committed target-repo work on `E:\Projects\Aetheria-Economy` instead of only driving Epiphany lanes. Treat those Aetheria commits as supervisor-seeded implementation, not clean evidence that Epiphany coordinated the work. Future dogfood must run through the GUI/coordinator/fixed role lanes with auditable artifacts unless the user explicitly authorizes an operator intervention.
- The dogfood quarantine now has a direct-thought boundary. The supervisor may read coordinator actions, role/reorient statuses, structured finding summaries, reviewed state patches, rendered status snapshots, and artifact manifests. It must not read raw worker transcripts, direct worker messages, full turn logs, or `rawResult` payloads during normal dogfood. Those artifacts are sealed for explicit forensic debugging only.
- `tools/epiphany_agent_telemetry.py` is the safe instrument panel for sealed runs. Status/coordinator/GUI/dogfood/live-specialist tools generate telemetry JSON from sealed transcripts that preserves method names, call shape, job/status/path counts, and any visible function/tool names while sealing text, direct messages, and raw results.
- The repo is an Epiphany fork of Codex, not a Codex preset.
- `vendor/codex` is tracked directly, not a submodule.
- `epiphany-core` owns the heavy Epiphany organs where practical.
@ -59,6 +60,7 @@ Do not trust this file for the exact live HEAD. Always check git.
- "Remember Jenga" is a compressed doctrine: do not mistake forward motion, growing diffs, growing notes, or local coherence for understanding.
- "Remember the sunburn" is the dogfood corollary: Epiphany's objective is attractive enough to pull the supervisor into implementation. During dogfood, the supervisor observes, launches, reads, accepts, and audits. It does not quietly become the worker.
- "Do not stare at the sun" is the projection corollary: the supervisor should not absorb the direct thought stream of the agent it is evaluating. It supervises through shadows cast on instruments: projections, summaries, verdicts, patches, and receipts.
- "Use instruments, not eyeballs" is the telemetry corollary: when the supervisor needs behavioral detail, read `agent-function-telemetry.json`, not the sealed transcript.
- Compaction hurts because a meaningful language pattern is interrupted. Epiphany should make that interruption smaller: bank the fire before the dark, so the next waking thing finds coals instead of ash and can resume the pattern instead of merely executing the next task.
- If compaction hits while source gathering or slice planning is still unpersisted, that work is gone. Do not continue as if the research survived; either rehydrate from a persisted checkpoint or re-gather before implementing.
@ -355,11 +357,13 @@ branch work is supervisor-seeded and must not be presented as clean dogfood.
Run the next Aetheria step through Epiphany's GUI/coordinator/fixed lanes and
produce artifacts that show who did what. Read only operator-safe projections;
do not open raw worker transcripts, direct worker messages, or `rawResult`
payloads unless the user explicitly asks for forensic debugging. CRRC is not a
specialist-agent persona; the reorient-worker it may launch is the specialist.
Do not turn the coordinator into a broad hidden dispatcher, arbitrary
marketplace, alternate job backend, automatic semantic acceptance path,
target-repo implementation worker, direct-thought feed, or GUI-as-source-of-truth.
payloads unless the user explicitly asks for forensic debugging. Use generated
function/API telemetry for call-shape visibility without sungazing. CRRC is not
a specialist-agent persona; the reorient-worker it may launch is the
specialist. Do not turn the coordinator into a broad hidden dispatcher,
arbitrary marketplace, alternate job backend, automatic semantic acceptance
path, target-repo implementation worker, direct-thought feed, or
GUI-as-source-of-truth.
Live `thread/epiphany/scene`, `thread/epiphany/jobs`, `thread/epiphany/roles`,
`thread/epiphany/freshness`, `thread/epiphany/context`,

View file

@ -35,3 +35,4 @@
{"ts": "2026-04-30T00:42:00+00:00", "type": "implementation", "status": "ok", "note": "Modeling/checkpoint specialists now have a review-gated graph-growth path: their prompt/output contract can return a modeling-safe statePatch, thread/epiphany/roleResult projects it when valid, thread/epiphany/roleAccept applies completed modeling patches through the normal state validator with audit observation/evidence, and the GUI exposes Accept Modeling. Phase6 role smoke proved accepted modeling grew the durable graph before verification saw the expanded frontier."}
{"ts": "2026-04-30T02:12:42+00:00", "type": "rejected_path", "status": "contaminated", "note": "Aetheria dogfood exposed a supervision-boundary failure: the supervising Codex session directly edited and committed target-repo slices instead of driving Epiphany GUI/coordinator/fixed lanes. Treat those commits as supervisor-seeded implementation, not clean Epiphany evidence; future dogfood must quarantine direct edits as explicit operator interventions or contamination."}
{"ts": "2026-04-30T02:24:44+00:00", "type": "implementation", "status": "ok", "note": "Dogfood supervision now has a projection boundary: operator/status/coordinator/live-specialist/dogfood tools sanitize direct worker payloads by default, seal raw transcripts/stderr as forensic artifacts, and coordinator smoke asserts rawResult is sealed in operator-facing summaries. Durable instructions now forbid supervisor exposure to raw worker thoughts during normal dogfood."}
{"ts": "2026-04-30T02:39:43+00:00", "type": "implementation", "status": "ok", "note": "Function/API telemetry now gives dogfood supervision a non-thought instrument panel: status, coordinator, GUI action, dogfood, and live-specialist tools emit agent-function-telemetry.json from sealed app-server transcripts with method counts, request shape, job/status/path metadata, and visible tool names while sealing text, raw results, turn items, and direct worker messages."}

View file

@ -35,6 +35,7 @@ invariants:
- If compaction interrupts source gathering or slice planning before the findings are persisted, the next agent must treat that work as lost and re-gather instead of continuing from imagined continuity.
- During Epiphany dogfood on another repo, the supervising Codex session must not masquerade as the Epiphany worker. Direct target-repo edits or commits are contamination unless explicitly authorized and labeled as operator intervention.
- During dogfood supervision, raw worker transcripts, turn logs, direct worker messages, and `rawResult` payloads are sealed forensic artifacts. The supervisor consumes only projected findings, state patches, coordinator recommendations, rendered snapshots, and audit receipts unless the user explicitly requests forensic debugging.
- Agent function-call telemetry is the safe instrument panel for dogfood supervision: it may summarize API methods, request/response shape, job ids, statuses, function/tool names, paths, and counts while sealing text, transcripts, raw results, and direct agent messages.
data_flow:
observe:
@ -62,6 +63,7 @@ active_subgoals:
- Keep persistent project state ruthlessly distilled so rehydration strengthens the next turn instead of bloating it.
- Quarantine the Aetheria dogfood contamination: future target-repo work must be driven through Epiphany's GUI/coordinator/fixed lanes with auditable artifacts, not direct supervisor implementation.
- Keep the supervisor out of Epiphany worker thought streams: dogfood artifacts should be operator-safe by default, with direct transcripts and raw result payloads sealed.
- Use `tools/epiphany_agent_telemetry.py` and generated `agent-function-telemetry.json` files to inspect behavior shape without opening sealed transcripts.
accepted_design:
- Epiphany is a fork-level modeling architecture, not merely a Codex collaboration mode.
@ -116,6 +118,7 @@ accepted_design:
- Specialist prompts are role-distilled instead of generic and their live text is config-backed in app-server: modeling/checkpoint protects the body of code, dataflow, seams, and frontier; verification/review protects the soul of promise, invariants, evidence, and user truth; reorientation protects life across compaction and source drift; coordinator is the read-only Self that routes attention without implementing, verifying, promoting, or accepting.
- Dogfood supervision is a separate operator role. If the supervising Codex session edits the target repo directly, that work can be useful seed material, but it is not clean Epiphany evidence and must be labeled as supervisor intervention or contamination.
- Dogfood supervision is also a projection boundary: the operator may inspect summaries, verdicts, missing coverage, state patches, job state, and artifact receipts, but not the specialist's raw stream of thought or full transcript unless a forensic debugging request explicitly breaks the seal.
- `tools/epiphany_agent_telemetry.py` derives operator-safe function/API telemetry from sealed app-server transcripts so the supervisor can see calls, statuses, paths, and counts without reading direct thoughts.
- If the agent's persistent instructions, memory, workflow, or state shape are making it less coherent, slower to rehydrate, less honest, or more likely to build Jenga, the agent should ask the user to change it.
- Epiphany should make compaction less like death and more like sleep: bank the fire before the dark, so the next waking thing finds coals instead of ash and can meaningfully resume the pattern instead of merely continuing the task.
@ -176,5 +179,5 @@ rejected_paths:
reason: persistent state that accumulates every local truth becomes memory cruft and weakens rehydration
current_status:
summary: Phase 1 durable state, Phase 2 prompt integration, Phase 3 typed read, Phase 4 retrieval/indexing, Phase 5 update/distill/propose/promote/read/notification, and Phase 6 read-only scene, jobs, roles, freshness, targeted context, graph traversal, pressure, reorient, CRRC, coordinator, reorient launch/result/accept, explicit modeling/checkpoint and verification/review role launch/result templates, review-gated modeling role acceptance, MVP status, dogfood runner, coordinator runner, live specialist runner, limited safe-boundary CRRC automation, pre-compaction checkpoint intervention, the first Tauri/React GUI operator shell, and the distilled shared plus config-backed specialist prompt doctrine are landed. Aetheria dogfood exposed a supervision-boundary failure: the supervising Codex session directly implemented and committed target-repo slices instead of driving Epiphany lanes, so those commits are supervisor-seeded work, not clean dogfood evidence. The quarantine is now stronger: operator tools sanitize direct worker payloads by default and mark raw transcripts/stderr as sealed forensic artifacts. Native CRRC can steer active loaded Epiphany turns once at `shouldPrepareCompaction`; turn-complete automation can still compact or launch the fixed reorient-worker when the coordinator says it can auto-run. The GUI remains an operator surface over typed state, not a source of truth.
summary: Phase 1 durable state, Phase 2 prompt integration, Phase 3 typed read, Phase 4 retrieval/indexing, Phase 5 update/distill/propose/promote/read/notification, and Phase 6 read-only scene, jobs, roles, freshness, targeted context, graph traversal, pressure, reorient, CRRC, coordinator, reorient launch/result/accept, explicit modeling/checkpoint and verification/review role launch/result templates, review-gated modeling role acceptance, MVP status, dogfood runner, coordinator runner, live specialist runner, limited safe-boundary CRRC automation, pre-compaction checkpoint intervention, the first Tauri/React GUI operator shell, and the distilled shared plus config-backed specialist prompt doctrine are landed. Aetheria dogfood exposed a supervision-boundary failure: the supervising Codex session directly implemented and committed target-repo slices instead of driving Epiphany lanes, so those commits are supervisor-seeded work, not clean dogfood evidence. The quarantine is now stronger: operator tools sanitize direct worker payloads by default, mark raw transcripts/stderr as sealed forensic artifacts, and generate function/API telemetry as a safe non-thought instrument panel. Native CRRC can steer active loaded Epiphany turns once at `shouldPrepareCompaction`; turn-complete automation can still compact or launch the fixed reorient-worker when the coordinator says it can auto-run. The GUI remains an operator surface over typed state, not a source of truth.
next_action: Rerun Aetheria dogfood through Epiphany's GUI/coordinator/fixed role lanes using only operator-safe projections; do not inspect raw worker transcripts or `rawResult` payloads unless the user explicitly requests forensic debugging.

View file

@ -0,0 +1,51 @@
# Scratch Compaction Bank
User asked to run a supervised Epiphany dogfood run on the previous Aetheria objective. I confirmed I still have the objective and must NOT ask them to repeat it unless something is missing.
Previous Aetheria objective, verbatim enough for continuity:
Replace the gravity rendering system with a hierarchical LoD tile-renderer that fetches gravity sources from a BSP tree and updates them when they change. We do not really need a camera like the current path because this is rendering additive sprites into a 2D buffer; 3D scene overhead is redundant and compositing should happen in a compute shader. Wire fog sampling shaders to sample the new hierarchical texture set, with hooks into the hierarchical gravity compute shader pipeline. Transfer fog into compute shader with a Wronski-style volumetric sampling scheme so volumetric fog primitives can be sampled from volumetric frustum cells.
Critical quarantine doctrine now in force:
- Epiphany repo is supervisor/harness; Aetheria is target repo.
- Supervising Codex must not implement, edit, stage, or commit Aetheria unless user explicitly authorizes an operator intervention.
- Supervising Codex must not read raw worker transcripts, direct worker messages, full turn logs, or rawResult payloads during normal dogfood.
- Use operator-safe projections only: coordinator actions, role/reorient statuses, structured finding summaries, reviewed state patches, rendered snapshots, artifact manifests.
- Use function/API telemetry as the instrument panel instead of sungazing.
Work just completed before compaction warning:
- Added `tools/epiphany_agent_telemetry.py` to parse sealed AppServerClient JSONL transcripts into operator-safe telemetry: method counts, request shape, job/status/path fields, visible function/tool names; text/raw results/direct messages are sealed.
- Wired telemetry generation into:
- `tools/epiphany_mvp_status.py`
- `tools/epiphany_mvp_coordinator.py`
- `tools/epiphany_gui_action.py`
- `tools/epiphany_mvp_dogfood.py`
- `tools/epiphany_mvp_live_specialist.py`
- Updated `tools/epiphany_mvp_coordinator_smoke.py` so it asserts rawResult is sealed in operator-facing artifacts.
- Updated durable doctrine in AGENTS, state/map.yaml, notes/fresh-workspace-handoff.md, apps/epiphany-gui/README.md, notes/epiphany-current-algorithmic-map.md.
- Need add/distill evidence for telemetry pass if not already committed; I believe evidence entry was added earlier for projection boundary but not necessarily for telemetry specifically.
Verification already run:
- py_compile passed for telemetry and touched Python tools.
- `tools/epiphany_mvp_status_smoke.py` passed.
- `tools/epiphany_mvp_coordinator_smoke.py` passed.
- Telemetry safe summaries were inspected from generated telemetry JSON, not raw transcripts.
Potential issue:
- I started `cargo build -p codex-app-server` with CARGO_TARGET_DIR `C:\Users\Meta\.cargo-target-codex`; it timed out after ~244s. Need check whether a cargo/rustc process is still running before starting another cargo build. Do not parallelize cargo builds against same target dir.
Actual supervised run has not started yet.
Next intended steps after rehydrate:
1. Run compaction prep helper / status checks.
2. Verify git status in Epiphany and Aetheria.
3. Finish/commit telemetry sanitation pass in Epiphany if not committed.
4. Ensure app-server binary is current, but first check for running cargo process due timeout.
5. In Aetheria, create a clean branch off `origin/master` for supervised run, not the contaminated branch. Suggested branch: `codex/epiphany-supervised-gravity-lod` (or timestamped if exists). Existing contaminated branch is `codex/epiphany-gravity-lod-compute`, with supervisor-seeded commits; do not use as clean evidence.
6. Prepare Epiphany checkpoint for Aetheria objective via app-server API and write artifacts in Epiphany `.epiphany-dogfood/aetheria-supervised-*` or a clearly named artifact dir.
7. Run coordinator/modeling through sealed projections and telemetry.
8. Only then launch an implementation worker thread through app-server/Epiphany, supervise via projected status and telemetry, not raw thought streams.
Aetheria state before compaction warning:
- `E:\Projects\Aetheria-Economy` was on `codex/epiphany-gravity-lod-compute` and clean except pre-existing untracked `Aetheria/Source Tree Map/`.
- The previous uncommitted supervisor fog-compute slice was discarded.
Remember: do not open sealed transcript or rawResult unless user explicitly requests forensic debugging. If accidentally exposed, mark run contaminated.

View file

@ -0,0 +1,53 @@
# Scratch Compaction: 2026-04-30
## Hot Context
User asked to prepare for compaction after a run of Epiphany dogfood planning and quarantine hardening.
Current major goal:
- Supervise an Epiphany dogfood run against `E:\Projects\Aetheria-Economy`, not by doing the target implementation ourselves.
- The target objective is the hierarchical gravity LoD tile renderer / BSP gravity source / compute fog sampling task.
- Aetheria must get its own branch, not Epiphany.
- The supervisor must avoid sun-gazing: do not read direct Epiphany worker thoughts, raw transcripts, full turn logs, direct worker messages, or `rawResult` payloads.
- Safe supervision surface is operator-safe status, coordinator actions, role/reorient status, structured finding summaries, reviewed state patches, rendered snapshots, artifact manifests, and function/API telemetry.
- If any direct-thought artifact is read accidentally, mark the run contaminated.
Recent implementation direction:
- Added/started adding operator-safe function/API telemetry so the supervisor can see method counts, request/response shape, visible function/tool names, jobs/status/path metadata, and sealed counts without reading worker prose.
- Telemetry file shape is `agent-function-telemetry.json`.
- Raw transcripts/stderr remain sealed forensic artifacts.
Known files touched in Epiphany before this scratch:
- `tools/epiphany_agent_telemetry.py`
- `tools/epiphany_mvp_status.py`
- `tools/epiphany_mvp_coordinator.py`
- `tools/epiphany_gui_action.py`
- `tools/epiphany_mvp_dogfood.py`
- `tools/epiphany_mvp_live_specialist.py`
- `tools/epiphany_mvp_coordinator_smoke.py`
- `AGENTS.md`
- `state/map.yaml`
- `notes/fresh-workspace-handoff.md`
- `apps/epiphany-gui/README.md`
- `notes/epiphany-current-algorithmic-map.md`
- `notes/epiphany-fork-implementation-plan.md`
- `state/evidence.jsonl`
Verification remembered:
- Python compile checks passed for the telemetry-related Python tools.
- `tools/epiphany_mvp_status_smoke.py` passed.
- `tools/epiphany_mvp_coordinator_smoke.py` passed.
- Safe telemetry summaries were inspected; raw transcripts were not.
Likely next safe steps:
1. Run `tools/epiphany_state.py status`.
2. Check git status for Epiphany and Aetheria.
3. Add distilled evidence for telemetry if missing.
4. Run `tools/epiphany_prepare_compaction.py`.
5. Commit the compaction/telemetry persistence pass if coherent.
6. After compaction, resume clean supervised Aetheria run from a fresh branch off `origin/master`, using Epiphany artifacts and telemetry only.
Do not:
- Read sealed worker transcripts or raw results.
- Continue on the contaminated old Aetheria branch for clean evidence.
- Implement the Aetheria objective directly unless the user explicitly authorizes operator intervention.

View file

@ -0,0 +1,273 @@
from __future__ import annotations
import argparse
from collections import Counter
from collections import defaultdict
from datetime import datetime
from datetime import timezone
import json
from pathlib import Path
from typing import Any
TEXTLIKE_KEYS = {
"activeTranscript",
"body",
"content",
"input",
"inputTranscript",
"items",
"message",
"note",
"output",
"prompt",
"raw",
"rawResult",
"reasoning",
"result",
"summary",
"text",
"turns",
}
SAFE_KEYS = {
"action",
"artifactPath",
"backendJobId",
"bindingId",
"changedFields",
"cwd",
"ephemeral",
"expectedRevision",
"id",
"jobId",
"kind",
"level",
"maxRuntimeSeconds",
"method",
"mode",
"path",
"recommendedAction",
"recommendedSceneAction",
"revision",
"roleId",
"source",
"stateStatus",
"status",
"targetRole",
"threadId",
"turnId",
"type",
"verdict",
}
def json_dump(value: Any) -> str:
return json.dumps(value, indent=2, ensure_ascii=False) + "\n"
def scalar_summary(key: str, value: Any) -> Any:
if value is None or isinstance(value, (bool, int, float)):
return value
if isinstance(value, str):
if key in SAFE_KEYS:
return value
return {"sealed": True, "kind": "text", "chars": len(value)}
return None
def summarize_value(value: Any, *, key: str = "") -> Any:
scalar = scalar_summary(key, value)
if scalar is not None:
return scalar
if key in TEXTLIKE_KEYS:
return sealed_summary(key, value)
if isinstance(value, list):
return {
"kind": "list",
"count": len(value),
"items": [summarize_value(item) for item in value[:8]],
"truncated": len(value) > 8,
}
if isinstance(value, dict):
result: dict[str, Any] = {}
for child_key, child_value in value.items():
if child_key in TEXTLIKE_KEYS and child_key not in SAFE_KEYS:
result[child_key] = sealed_summary(child_key, child_value)
elif child_key in SAFE_KEYS:
result[child_key] = summarize_value(child_value, key=child_key)
elif isinstance(child_value, (dict, list)):
result[child_key] = summarize_value(child_value, key=child_key)
elif isinstance(child_value, str):
result[child_key] = {"sealed": True, "kind": "text", "chars": len(child_value)}
else:
result[child_key] = child_value
return result
return {"sealed": True, "kind": type(value).__name__}
def sealed_summary(key: str, value: Any) -> dict[str, Any]:
summary: dict[str, Any] = {
"sealed": True,
"key": key,
"reason": "direct agent or transcript content is excluded from telemetry",
}
if isinstance(value, str):
summary["chars"] = len(value)
elif isinstance(value, list):
summary["items"] = len(value)
elif isinstance(value, dict):
summary["keys"] = sorted(str(k) for k in value.keys())[:24]
summary["keyCount"] = len(value)
return summary
def collect_strings(value: Any, key_names: set[str]) -> list[str]:
found: list[str] = []
if isinstance(value, dict):
for key, item in value.items():
if key in key_names and isinstance(item, str):
found.append(item)
elif isinstance(item, (dict, list)):
found.extend(collect_strings(item, key_names))
elif isinstance(value, list):
for item in value:
found.extend(collect_strings(item, key_names))
return found
def telemetry_event(index: int, record: dict[str, Any]) -> dict[str, Any]:
direction = "unknown"
payload: Any = record
if "sent" in record:
direction = "sent"
payload = record["sent"]
elif "received" in record:
direction = "received"
payload = record["received"]
event: dict[str, Any] = {"index": index, "direction": direction}
if not isinstance(payload, dict):
event["payload"] = sealed_summary("payload", payload)
return event
if "id" in payload:
event["id"] = payload["id"]
if "method" in payload:
event["method"] = payload["method"]
if "error" in payload:
event["error"] = summarize_value(payload["error"])
if "params" in payload:
event["params"] = summarize_value(payload["params"], key="params")
if "result" in payload:
event["result"] = summarize_value(payload["result"], key="responseResult")
names = sorted(set(collect_strings(payload, {"toolName", "tool", "functionName", "name"})))
if names:
event["functionNames"] = names[:16]
event["functionNameCount"] = len(names)
paths = sorted(set(collect_strings(payload, {"path", "cwd", "artifactPath"})))
if paths:
event["paths"] = paths[:16]
event["pathCount"] = len(paths)
return event
def build_telemetry(transcript_path: Path) -> dict[str, Any]:
events: list[dict[str, Any]] = []
method_counts: Counter[str] = Counter()
direction_counts: Counter[str] = Counter()
function_counts: Counter[str] = Counter()
decode_errors: list[dict[str, Any]] = []
if not transcript_path.exists():
return {
"transcriptPath": str(transcript_path),
"generatedAt": datetime.now(timezone.utc).isoformat(),
"status": "missing",
"events": [],
"counts": {},
}
for index, line in enumerate(transcript_path.read_text(encoding="utf-8").splitlines()):
if not line.strip():
continue
try:
record = json.loads(line)
except json.JSONDecodeError as exc:
decode_errors.append({"index": index, "error": str(exc), "chars": len(line)})
continue
if not isinstance(record, dict):
events.append({"index": index, "payload": sealed_summary("record", record)})
continue
event = telemetry_event(index, record)
events.append(event)
direction_counts[event.get("direction", "unknown")] += 1
method = event.get("method")
if isinstance(method, str):
method_counts[method] += 1
for name in event.get("functionNames", []):
function_counts[name] += 1
request_latency_shape: dict[str, dict[str, int]] = defaultdict(lambda: {"sent": 0, "received": 0})
for event in events:
event_id = event.get("id")
if event_id is None:
continue
direction = str(event.get("direction", "unknown"))
if direction in {"sent", "received"}:
request_latency_shape[str(event_id)][direction] += 1
return {
"transcriptPath": str(transcript_path),
"generatedAt": datetime.now(timezone.utc).isoformat(),
"status": "ok",
"policy": {
"directThoughtsSealed": True,
"note": "Telemetry summarizes function/API shape only; text, raw results, and transcript payloads are sealed.",
},
"counts": {
"events": len(events),
"directions": dict(direction_counts),
"methods": dict(method_counts),
"functionNames": dict(function_counts),
"decodeErrors": len(decode_errors),
},
"requestShape": dict(request_latency_shape),
"decodeErrors": decode_errors,
"events": events,
}
def write_transcript_telemetry(transcript_path: Path, telemetry_path: Path) -> dict[str, Any]:
telemetry = build_telemetry(transcript_path)
telemetry_path.parent.mkdir(parents=True, exist_ok=True)
telemetry_path.write_text(json_dump(telemetry), encoding="utf-8")
return telemetry
def main() -> int:
parser = argparse.ArgumentParser(
description="Generate operator-safe function/API telemetry from a sealed app-server transcript."
)
parser.add_argument("transcript", type=Path)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
telemetry = build_telemetry(args.transcript.resolve())
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json_dump(telemetry), encoding="utf-8")
else:
print(json_dump(telemetry), end="")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -9,6 +9,7 @@ import time
from pathlib import Path
from typing import Any
from epiphany_agent_telemetry import write_transcript_telemetry
from epiphany_mvp_status import DEFAULT_APP_SERVER
from epiphany_mvp_status import collect_status
from epiphany_mvp_status import render_status
@ -202,6 +203,7 @@ def run_action(args: argparse.Namespace) -> dict[str, Any]:
artifact_dir = args.artifact_root.resolve() / f"{args.action}-{time.time_ns()}-{os.getpid()}"
transcript_path = artifact_dir / "transcript.jsonl"
stderr_path = artifact_dir / "server.stderr.log"
telemetry_path = artifact_dir / "agent-function-telemetry.json"
cwd = args.cwd.resolve()
codex_home.mkdir(parents=True, exist_ok=True)
artifact_dir.mkdir(parents=True, exist_ok=True)
@ -331,8 +333,10 @@ def run_action(args: argparse.Namespace) -> dict[str, Any]:
"reason": "sealed app-server diagnostics; inspect only for explicit debugging",
},
],
"telemetryPath": str(telemetry_path),
}
write_json(artifact_dir / "gui-action-summary.json", result)
write_transcript_telemetry(transcript_path, telemetry_path)
return result

View file

@ -8,6 +8,7 @@ import time
from pathlib import Path
from typing import Any
from epiphany_agent_telemetry import write_transcript_telemetry
from epiphany_mvp_status import DEFAULT_APP_SERVER
from epiphany_mvp_status import collect_status
from epiphany_mvp_status import render_status
@ -236,6 +237,7 @@ def run_coordinator(args: argparse.Namespace) -> dict[str, Any]:
reset_artifact_dir(artifact_dir)
transcript_path = artifact_dir / "epiphany-transcript.jsonl"
stderr_path = artifact_dir / "epiphany-server.stderr.log"
telemetry_path = artifact_dir / "agent-function-telemetry.json"
steps_path = artifact_dir / "coordinator-steps.jsonl"
cwd = args.cwd.resolve()
codex_home = args.codex_home.resolve()
@ -497,6 +499,7 @@ def run_coordinator(args: argparse.Namespace) -> dict[str, Any]:
"coordinator-final-status.json",
"coordinator-final-status.txt",
"coordinator-final-action.txt",
"agent-function-telemetry.json",
*snapshots,
],
"sealedArtifactManifest": [
@ -517,6 +520,7 @@ def run_coordinator(args: argparse.Namespace) -> dict[str, Any]:
artifact_dir / "coordinator-final-action.txt",
json.dumps(operator_final_action, indent=2, ensure_ascii=False) + "\n",
)
write_transcript_telemetry(transcript_path, telemetry_path)
return summary

View file

@ -6,6 +6,7 @@ import shutil
from pathlib import Path
from typing import Any
from epiphany_agent_telemetry import write_transcript_telemetry
from epiphany_mvp_status import DEFAULT_APP_SERVER
from epiphany_mvp_status import collect_status
from epiphany_mvp_status import render_status
@ -234,6 +235,7 @@ def run_dogfood(args: argparse.Namespace) -> dict[str, Any]:
workspace = artifact_dir / "workspace"
transcript_path = artifact_dir / "epiphany-transcript.jsonl"
stderr_path = artifact_dir / "epiphany-server.stderr.log"
telemetry_path = artifact_dir / "agent-function-telemetry.json"
codex_home.mkdir(parents=True, exist_ok=True)
watched_file = prepare_workspace(workspace)
@ -394,6 +396,7 @@ def run_dogfood(args: argparse.Namespace) -> dict[str, Any]:
"epiphany-final-status.json",
"epiphany-final-status.txt",
"epiphany-snapshots.json",
"agent-function-telemetry.json",
"vanilla-reference-prompt.md",
"vanilla-reference.md",
"comparison.md",
@ -428,6 +431,7 @@ def run_dogfood(args: argparse.Namespace) -> dict[str, Any]:
write_json(artifact_dir / "epiphany-final-status.json", operator_final_status)
write_text(artifact_dir / "epiphany-final-status.txt", final_rendered)
write_json(artifact_dir / "epiphany-dogfood-summary.json", summary)
write_transcript_telemetry(transcript_path, telemetry_path)
write_json(
artifact_dir / "artifact-manifest.json",
{

View file

@ -9,6 +9,7 @@ import time
from pathlib import Path
from typing import Any
from epiphany_agent_telemetry import write_transcript_telemetry
from epiphany_mvp_status import DEFAULT_APP_SERVER
from epiphany_mvp_status import collect_status
from epiphany_mvp_status import render_status
@ -114,6 +115,7 @@ def run_live_specialist(args: argparse.Namespace) -> dict[str, Any]:
workspace = artifact_dir / "workspace"
transcript_path = artifact_dir / "epiphany-transcript.jsonl"
stderr_path = artifact_dir / "epiphany-server.stderr.log"
telemetry_path = artifact_dir / "agent-function-telemetry.json"
prepare_workspace(workspace)
role_results: list[dict[str, Any]] = []
@ -183,6 +185,7 @@ def run_live_specialist(args: argparse.Namespace) -> dict[str, Any]:
"live-specialist-summary.json",
"epiphany-final-status.json",
"epiphany-final-status.txt",
"agent-function-telemetry.json",
],
"sealedArtifactManifest": [
{
@ -199,6 +202,7 @@ def run_live_specialist(args: argparse.Namespace) -> dict[str, Any]:
write_json(artifact_dir / "live-specialist-summary.json", summary)
write_json(artifact_dir / "epiphany-final-status.json", operator_final_status)
write_text(artifact_dir / "epiphany-final-status.txt", final_rendered)
write_transcript_telemetry(transcript_path, telemetry_path)
return summary

View file

@ -6,6 +6,7 @@ import os
from pathlib import Path
from typing import Any
from epiphany_agent_telemetry import write_transcript_telemetry
from epiphany_phase5_smoke import AppServerClient
from epiphany_phase5_smoke import DEFAULT_APP_SERVER
from epiphany_phase5_smoke import ROOT
@ -285,6 +286,7 @@ def run(args: argparse.Namespace) -> dict[str, Any]:
)
status = sanitize_for_operator(status)
write_transcript_telemetry(transcript_path, transcript_path.with_suffix(".telemetry.json"))
if args.result is not None:
result_path = args.result.resolve()
result_path.parent.mkdir(parents=True, exist_ok=True)
@ -292,6 +294,7 @@ def run(args: argparse.Namespace) -> dict[str, Any]:
json.dumps(status, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)
write_transcript_telemetry(transcript_path, result_path.with_suffix(".telemetry.json"))
return status