Route birth runner through OpenAI runtime
This commit is contained in:
parent
8516da46ff
commit
1813915b00
8 changed files with 209 additions and 20 deletions
|
|
@ -8,12 +8,14 @@ use std::env;
|
|||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Child;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const DEFAULT_CODEX_BIN: &str = "codex";
|
||||
const DEFAULT_OPENAI_RUNTIME_BIN: &str = "epiphany-openai-runtime";
|
||||
const DEFAULT_TIMEOUT_SECONDS: u64 = 900;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
|
|
@ -31,7 +33,10 @@ struct Args {
|
|||
init_store: PathBuf,
|
||||
agent_store: PathBuf,
|
||||
heartbeat_store: PathBuf,
|
||||
runtime_store: PathBuf,
|
||||
codex_bin: String,
|
||||
openai_runtime_bin: String,
|
||||
executor: String,
|
||||
mode: String,
|
||||
model: Option<String>,
|
||||
timeout_seconds: u64,
|
||||
|
|
@ -52,7 +57,10 @@ impl Args {
|
|||
init_store: root.join("state").join("repo-initialization.msgpack"),
|
||||
agent_store: root.join("state").join("agents.msgpack"),
|
||||
heartbeat_store: root.join("state").join("agent-heartbeats.msgpack"),
|
||||
runtime_store: root.join("state").join("runtime-spine.msgpack"),
|
||||
codex_bin: DEFAULT_CODEX_BIN.to_string(),
|
||||
openai_runtime_bin: DEFAULT_OPENAI_RUNTIME_BIN.to_string(),
|
||||
executor: "openai-runtime".to_string(),
|
||||
mode: "plan".to_string(),
|
||||
model: None,
|
||||
timeout_seconds: DEFAULT_TIMEOUT_SECONDS,
|
||||
|
|
@ -68,7 +76,14 @@ impl Args {
|
|||
"--heartbeat-store" => {
|
||||
parsed.heartbeat_store = take_path(&mut args, "--heartbeat-store")?;
|
||||
}
|
||||
"--runtime-store" => {
|
||||
parsed.runtime_store = take_path(&mut args, "--runtime-store")?
|
||||
}
|
||||
"--codex-bin" => parsed.codex_bin = take_string(&mut args, "--codex-bin")?,
|
||||
"--openai-runtime-bin" => {
|
||||
parsed.openai_runtime_bin = take_string(&mut args, "--openai-runtime-bin")?;
|
||||
}
|
||||
"--executor" => parsed.executor = take_string(&mut args, "--executor")?,
|
||||
"--mode" => parsed.mode = take_string(&mut args, "--mode")?,
|
||||
"--model" => parsed.model = Some(take_string(&mut args, "--model")?),
|
||||
"--timeout-seconds" => {
|
||||
|
|
@ -82,6 +97,9 @@ impl Args {
|
|||
if !matches!(parsed.mode.as_str(), "plan" | "run") {
|
||||
return Err(anyhow!("--mode must be plan or run"));
|
||||
}
|
||||
if !matches!(parsed.executor.as_str(), "openai-runtime" | "codex-exec") {
|
||||
return Err(anyhow!("--executor must be openai-runtime or codex-exec"));
|
||||
}
|
||||
Ok(parsed)
|
||||
}
|
||||
}
|
||||
|
|
@ -116,8 +134,10 @@ fn run(args: Args) -> Result<Value> {
|
|||
let prompt_path = execution_dir.join("prompt.md");
|
||||
let schema_path = execution_dir.join("output-schema.json");
|
||||
let result_path = execution_dir.join("result.json");
|
||||
let stdout_path = execution_dir.join("codex.stdout.log");
|
||||
let stderr_path = execution_dir.join("codex.stderr.log");
|
||||
let request_path = execution_dir.join("openai-request.json");
|
||||
let runtime_summary_path = execution_dir.join("openai-runtime-summary.json");
|
||||
let stdout_path = execution_dir.join("executor.stdout.log");
|
||||
let stderr_path = execution_dir.join("executor.stderr.log");
|
||||
let prompt = render_specialist_prompt(kind, &packet_value)?;
|
||||
let schema = output_schema_for_kind(kind, &packet_value)?;
|
||||
write_text(&prompt_path, &prompt)?;
|
||||
|
|
@ -131,20 +151,37 @@ fn run(args: Args) -> Result<Value> {
|
|||
"promptPath": prompt_path,
|
||||
"schemaPath": schema_path,
|
||||
"resultPath": result_path,
|
||||
"requestPath": request_path,
|
||||
"runtimeSummaryPath": runtime_summary_path,
|
||||
"stdoutPath": stdout_path,
|
||||
"stderrPath": stderr_path,
|
||||
"executor": args.executor.clone(),
|
||||
"status": "planned",
|
||||
"acceptCommand": accept_command(kind, &args, &packet_path, &result_path),
|
||||
});
|
||||
if args.mode == "run" {
|
||||
let run_result = run_codex_specialist(
|
||||
&args,
|
||||
&prompt,
|
||||
&schema_path,
|
||||
&result_path,
|
||||
&stdout_path,
|
||||
&stderr_path,
|
||||
)?;
|
||||
let run_result = if args.executor == "codex-exec" {
|
||||
run_codex_specialist(
|
||||
&args,
|
||||
&prompt,
|
||||
&schema_path,
|
||||
&result_path,
|
||||
&stdout_path,
|
||||
&stderr_path,
|
||||
)?
|
||||
} else {
|
||||
run_openai_runtime_specialist(
|
||||
&args,
|
||||
kind,
|
||||
&prompt,
|
||||
&schema_path,
|
||||
&request_path,
|
||||
&result_path,
|
||||
&runtime_summary_path,
|
||||
&stdout_path,
|
||||
&stderr_path,
|
||||
)?
|
||||
};
|
||||
execution["status"] = run_result["status"].clone();
|
||||
execution["exitCode"] = run_result["exitCode"].clone();
|
||||
execution["timedOut"] = run_result["timedOut"].clone();
|
||||
|
|
@ -177,6 +214,8 @@ fn run(args: Args) -> Result<Value> {
|
|||
"initStore": args.init_store,
|
||||
"agentStore": args.agent_store,
|
||||
"heartbeatStore": args.heartbeat_store,
|
||||
"runtimeStore": args.runtime_store,
|
||||
"executor": args.executor,
|
||||
"startup": startup,
|
||||
"executions": executions,
|
||||
"requiresReview": !args.auto_accept,
|
||||
|
|
@ -258,6 +297,115 @@ fn run_codex_specialist(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_openai_runtime_specialist(
|
||||
args: &Args,
|
||||
kind: &str,
|
||||
prompt: &str,
|
||||
schema_path: &Path,
|
||||
request_path: &Path,
|
||||
result_path: &Path,
|
||||
runtime_summary_path: &Path,
|
||||
stdout_path: &Path,
|
||||
stderr_path: &Path,
|
||||
) -> Result<Value> {
|
||||
let schema_text = fs::read_to_string(schema_path)
|
||||
.with_context(|| format!("failed to read {}", schema_path.display()))?;
|
||||
let model = args.model.clone().unwrap_or_else(|| "gpt-5.4".to_string());
|
||||
let request_id = format!(
|
||||
"repo-birth-{}-{}",
|
||||
sanitize(kind),
|
||||
Utc::now().timestamp_millis()
|
||||
);
|
||||
let request = json!({
|
||||
"schema_id": "epiphany.openai_model_request.v0",
|
||||
"request_id": request_id,
|
||||
"conversation_id": format!("repo-birth-{}", sanitize(kind)),
|
||||
"model": model,
|
||||
"instructions": format!(
|
||||
"{prompt}\n\nReturn only a single JSON object matching this output schema. No Markdown, no commentary.\n\n```json\n{schema_text}\n```"
|
||||
),
|
||||
"input": [
|
||||
{
|
||||
"UserText": {
|
||||
"text": "Execute this startup-only Epiphany birth distiller packet and return the reviewable JSON result."
|
||||
}
|
||||
}
|
||||
],
|
||||
"reasoning_effort": Value::Null,
|
||||
"reasoning_summary": Value::Null,
|
||||
"service_tier": Value::Null,
|
||||
"output_contract_id": format!("repo-birth-{kind}")
|
||||
});
|
||||
write_json(request_path, &request)?;
|
||||
|
||||
let mut command = Command::new(&args.openai_runtime_bin);
|
||||
command
|
||||
.arg("model-turn")
|
||||
.arg("--store")
|
||||
.arg(&args.runtime_store)
|
||||
.arg("--request")
|
||||
.arg(request_path)
|
||||
.arg("--objective")
|
||||
.arg(format!("Run startup-only repo birth specialist {kind}"))
|
||||
.arg("--default-model")
|
||||
.arg(&model)
|
||||
.arg("--output-last-message")
|
||||
.arg(result_path)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
let child = command
|
||||
.spawn()
|
||||
.context("failed to spawn epiphany-openai-runtime model-turn")?;
|
||||
wait_for_child(
|
||||
child,
|
||||
args.timeout_seconds,
|
||||
stdout_path,
|
||||
stderr_path,
|
||||
Some(runtime_summary_path),
|
||||
)
|
||||
}
|
||||
|
||||
fn wait_for_child(
|
||||
mut child: Child,
|
||||
timeout_seconds: u64,
|
||||
stdout_path: &Path,
|
||||
stderr_path: &Path,
|
||||
stdout_json_copy_path: Option<&Path>,
|
||||
) -> Result<Value> {
|
||||
let deadline = Instant::now() + Duration::from_secs(timeout_seconds);
|
||||
loop {
|
||||
if let Some(status) = child.try_wait()? {
|
||||
let output = child.wait_with_output()?;
|
||||
write_bytes(stdout_path, &output.stdout)?;
|
||||
write_bytes(stderr_path, &output.stderr)?;
|
||||
if let Some(path) = stdout_json_copy_path {
|
||||
write_bytes(path, &output.stdout)?;
|
||||
}
|
||||
return Ok(json!({
|
||||
"status": if status.success() { "completed" } else { "failed" },
|
||||
"exitCode": status.code(),
|
||||
"timedOut": false,
|
||||
}));
|
||||
}
|
||||
if Instant::now() >= deadline {
|
||||
let _ = child.kill();
|
||||
let output = child.wait_with_output()?;
|
||||
write_bytes(stdout_path, &output.stdout)?;
|
||||
write_bytes(stderr_path, &output.stderr)?;
|
||||
if let Some(path) = stdout_json_copy_path {
|
||||
write_bytes(path, &output.stdout)?;
|
||||
}
|
||||
return Ok(json!({
|
||||
"status": "timeout",
|
||||
"exitCode": Value::Null,
|
||||
"timedOut": true,
|
||||
}));
|
||||
}
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
}
|
||||
}
|
||||
|
||||
fn render_specialist_prompt(kind: &str, packet: &Value) -> Result<String> {
|
||||
Ok(format!(
|
||||
"{}\n\n# Startup-Only Birth Packet\n\nYou are executing exactly one repo initialization birth specialist packet. Do not edit files. Do not mutate state. Return only JSON that matches the provided schema. The coordinator/Self will review and decide whether to accept the result.\n\n```json\n{}\n```\n",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use epiphany_openai_adapter::EpiphanyOpenAiStreamEvent;
|
|||
use epiphany_openai_adapter::EpiphanyOpenAiStreamPayload;
|
||||
use epiphany_openai_runtime::EpiphanyOpenAiRuntimeOptions;
|
||||
use epiphany_openai_runtime::OPENAI_RUNTIME_ROLE;
|
||||
use epiphany_openai_runtime::assistant_text_from_openai_events;
|
||||
use epiphany_openai_runtime::default_codex_home;
|
||||
use epiphany_openai_runtime::default_options;
|
||||
use epiphany_openai_runtime::ensure_openai_runtime_ready;
|
||||
|
|
@ -33,8 +34,17 @@ async fn main() -> Result<()> {
|
|||
.with_context(|| format!("failed to read {}", options.request_path.display()))?;
|
||||
let request: EpiphanyOpenAiModelRequest = serde_json::from_str(&request_text)
|
||||
.with_context(|| format!("failed to parse {}", options.request_path.display()))?;
|
||||
let output_last_message_path = options.output_last_message_path.clone();
|
||||
let runtime_options = options.into_runtime_options(&request);
|
||||
let summary = run_openai_model_turn(runtime_options, request).await?;
|
||||
let summary = run_openai_model_turn(runtime_options.clone(), request.clone()).await?;
|
||||
if let Some(path) = output_last_message_path {
|
||||
let text = assistant_text_from_openai_events(
|
||||
&runtime_options.store_path,
|
||||
&request.request_id,
|
||||
)?;
|
||||
fs::write(&path, text)
|
||||
.with_context(|| format!("failed to write {}", path.display()))?;
|
||||
}
|
||||
print_json(&summary)?;
|
||||
}
|
||||
"smoke" => {
|
||||
|
|
@ -105,6 +115,7 @@ struct ModelTurnCliOptions {
|
|||
job_id: Option<String>,
|
||||
objective: Option<String>,
|
||||
default_model: Option<String>,
|
||||
output_last_message_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl ModelTurnCliOptions {
|
||||
|
|
@ -142,6 +153,7 @@ fn parse_model_turn_options(args: Vec<String>) -> Result<ModelTurnCliOptions> {
|
|||
let mut job_id = None;
|
||||
let mut objective = None;
|
||||
let mut default_model = None;
|
||||
let mut output_last_message_path = None;
|
||||
let mut iter = args.into_iter();
|
||||
while let Some(arg) = iter.next() {
|
||||
match arg.as_str() {
|
||||
|
|
@ -152,6 +164,12 @@ fn parse_model_turn_options(args: Vec<String>) -> Result<ModelTurnCliOptions> {
|
|||
"--job-id" => job_id = Some(next_value(&mut iter, "--job-id")?),
|
||||
"--objective" => objective = Some(next_value(&mut iter, "--objective")?),
|
||||
"--default-model" => default_model = Some(next_value(&mut iter, "--default-model")?),
|
||||
"--output-last-message" => {
|
||||
output_last_message_path = Some(PathBuf::from(next_value(
|
||||
&mut iter,
|
||||
"--output-last-message",
|
||||
)?))
|
||||
}
|
||||
other => return Err(anyhow!("unknown model-turn argument: {other}")),
|
||||
}
|
||||
}
|
||||
|
|
@ -163,6 +181,7 @@ fn parse_model_turn_options(args: Vec<String>) -> Result<ModelTurnCliOptions> {
|
|||
job_id,
|
||||
objective,
|
||||
default_model,
|
||||
output_last_message_path,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -201,5 +220,5 @@ fn now() -> String {
|
|||
}
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"usage: epiphany-openai-runtime <model-turn|smoke> [--store path] [--codex-home path] [--request path] [--session-id id] [--job-id id] [--objective text] [--default-model model]"
|
||||
"usage: epiphany-openai-runtime <model-turn|smoke> [--store path] [--codex-home path] [--request path] [--session-id id] [--job-id id] [--objective text] [--default-model model] [--output-last-message path]"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,6 +245,28 @@ pub fn store_openai_request(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn assistant_text_from_openai_events(
|
||||
store_path: impl AsRef<Path>,
|
||||
request_id: &str,
|
||||
) -> Result<String> {
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let mut events = cache
|
||||
.get_all::<EpiphanyOpenAiStreamEvent>()?
|
||||
.into_iter()
|
||||
.filter(|event| event.request_id == request_id)
|
||||
.collect::<Vec<_>>();
|
||||
events.sort_by_key(|event| event.sequence);
|
||||
|
||||
let mut text = String::new();
|
||||
for event in events {
|
||||
if let EpiphanyOpenAiStreamPayload::TextDelta { text: delta } = event.payload {
|
||||
text.push_str(&delta);
|
||||
}
|
||||
}
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
pub fn default_codex_home() -> Result<PathBuf> {
|
||||
if let Ok(path) = std::env::var("CODEX_HOME") {
|
||||
return Ok(PathBuf::from(path));
|
||||
|
|
@ -387,6 +409,7 @@ mod tests {
|
|||
let summary = record_openai_events(&store, &options, &request, &events)?;
|
||||
|
||||
assert_eq!(summary.verdict, "pass");
|
||||
assert_eq!(assistant_text_from_openai_events(&store, "req-1")?, "");
|
||||
let mut cache = runtime_spine_cache(&store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
assert!(cache.get::<EpiphanyOpenAiModelRequest>("req-1")?.is_some());
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ remembers doctrine; the branch remembers the blade.
|
|||
- Epiphany now owns the canonical schema paperwork locally under `schemas/`. The key receipts are `schemas/ghostlight.agent-state.schema.json`, `schemas/canonical-agent-state-schema.md`, `schemas/agent-state-variable-glossary.md`, `schemas/repo-personality-birth-projection.md`, and `schemas/heartbeat-state-schema.md`. If a standing trait name like `routing_discipline` changes, or if dossier/heartbeat/birth-projection semantics move, update those docs in the same pass instead of relying on old Ghostlight notes or live store archaeology.
|
||||
- The schema doctrine now makes the dossier split explicit: most resident Epiphany organs are `lane_core` dossiers with a lean role lattice and growth via rumination/distillation, while Face is an `embodied_actor` dossier that should trend toward dense Ghostlight-family personality, relationship pressure, and fallible character-loop response. `epiphany-agent-memory-store status` and `epiphany-character-loop` now surface that classification on the wire.
|
||||
- Face packets now carry deterministic Ghostlight-style `projectionSeed`, `appraisalSeed`, and `reactionSeed` surfaces rebuilt from local dossier traits, relationship memories, perceived overlays, and visible stimulus so the public mouth can use actual projection/appraisal machinery instead of decorative prompt text. Also record this now before some future fool hard-codes singularity: multiple Face actors are part of the plan, even though the current MVP still routes through one `face` role.
|
||||
- Repo personality initialization now has a terrain-reduced plan and native birth surface. `epiphany-repo-personality scout/project/agent-packet/status` scouts local git repos, scores body taxonomy/history temperament/memory doctrine, writes typed CultCache terrain/profile/role-projection MessagePack stores, emits JSON/Markdown inspection exports, and renders a birth-only Repo Personality Distiller specialist prompt packet. `epiphany-repo-personality startup` now checks for accepted personality/memory initialization records, launches packet generation only when absent, and `accept-init` can route reviewed role `selfPatch` candidates into initial role memory, stamp the newborn Ghostlight trait lattice from deterministic role projections, and seed heartbeat physiology from the same typed role personality projections. The birth specialists are startup-only initialization actuators, not heartbeat lanes; heartbeat receives only the accepted physiology seed. `epiphany-repo-birth-runner` now owns the startup-only execution path: plan mode writes Codex execution prompts/schemas/accept commands, and run mode invokes `codex exec` with the packet schema and captures reviewable `result.json`. The remaining seam is Aquarium review/action surfacing. After accepted birth, personality drift belongs to heartbeat, mood, rumination, sleep consolidation, lived evidence, and reviewed `selfPatch`, not repeated startup distillation.
|
||||
- Repo personality initialization now has a terrain-reduced plan and native birth surface. `epiphany-repo-personality scout/project/agent-packet/status` scouts local git repos, scores body taxonomy/history temperament/memory doctrine, writes typed CultCache terrain/profile/role-projection MessagePack stores, emits JSON/Markdown inspection exports, and renders a birth-only Repo Personality Distiller specialist prompt packet. `epiphany-repo-personality startup` now checks for accepted personality/memory initialization records, launches packet generation only when absent, and `accept-init` can route reviewed role `selfPatch` candidates into initial role memory, stamp the newborn Ghostlight trait lattice from deterministic role projections, and seed heartbeat physiology from the same typed role personality projections. The birth specialists are startup-only initialization actuators, not heartbeat lanes; heartbeat receives only the accepted physiology seed. `epiphany-repo-birth-runner` now owns the startup-only execution path: plan mode writes prompts/schemas/accept commands, and run mode defaults to `epiphany-openai-runtime` typed model-request documents plus runtime-spine receipts and reviewable `result.json`; `codex exec` remains only as explicit `--executor codex-exec` fallback. The remaining seam is Aquarium review/action surfacing. After accepted birth, personality drift belongs to heartbeat, mood, rumination, sleep consolidation, lived evidence, and reviewed `selfPatch`, not repeated startup distillation.
|
||||
- Repo trajectory initialization is now a separate birth-only organ. `epiphany-repo-personality` derives a typed `repo_trajectory_report` from early-history, recent-history, doctrine/content excerpts, and deterministic theme scoring, and `trajectory-packet` renders a startup-only Repo Trajectory Distiller prompt/packet so Self can review historical direction, self-image, implicit goals, and anti-goals before the newborn wakes.
|
||||
- Repo memory initialization is now split from personality initialization. `epiphany-repo-personality memory-packet` renders a birth-only Repo Memory Distiller packet from the same typed terrain/profile/projection store plus bounded source excerpts. It gives each organ its own mission filter: Self for routing/authority, Face for public surface, Imagination for plans/backlog, Eyes for prior art, Body for architecture, Hands for implementation habits, Soul for evidence, and Life for continuity. The output is still a Self-reviewed petition, not direct memory mutation.
|
||||
- The first birth startup valve is now native. `epiphany-repo-personality startup` checks a typed init store for accepted `repo-trajectory`, `repo-personality`, and `repo-memory` records, generates missing packets under a startup artifact dir, and returns `reviewInitializationPackets`. Generated birth packets now advertise `birthOnly`, `executionOwner: repo-initialization-startup-runner`, and no heartbeat participant. `epiphany-repo-birth-runner` consumes those packets as startup-only Codex exec jobs and writes prompts, output schemas, stdout/stderr logs, result files, and exact `accept-init` commands. `accept-init` can process a distiller result, review/apply role `selfPatch` candidates through `state/agents.msgpack`, apply `repo-personality` heartbeat seeds through `state/agent-heartbeats.msgpack`, and seal the reviewed packet as `epiphany.repo_initialization_record.v0`; after all required records exist, startup returns `continueStartup` and does not regenerate birth packets. The remaining UI wound is Aquarium review/action surfacing.
|
||||
- The first birth startup valve is now native. `epiphany-repo-personality startup` checks a typed init store for accepted `repo-trajectory`, `repo-personality`, and `repo-memory` records, generates missing packets under a startup artifact dir, and returns `reviewInitializationPackets`. Generated birth packets now advertise `birthOnly`, `executionOwner: repo-initialization-startup-runner`, and no heartbeat participant. `epiphany-repo-birth-runner` consumes those packets as startup-only typed OpenAI runtime jobs by default and writes prompts, output schemas, model-request documents, runtime summaries, stdout/stderr logs, result files, and exact `accept-init` commands. `accept-init` can process a distiller result, review/apply role `selfPatch` candidates through `state/agents.msgpack`, apply `repo-personality` heartbeat seeds through `state/agent-heartbeats.msgpack`, and seal the reviewed packet as `epiphany.repo_initialization_record.v0`; after all required records exist, startup returns `continueStartup` and does not regenerate birth packets. The remaining UI wound is Aquarium review/action surfacing.
|
||||
- Native CRRC automation is now landed only at turn-complete safe boundaries. It may submit `Op::Compact` for coordinator-approved `compactRehydrateReorient` or for a successful pre-compaction checkpoint intervention's pending compact handoff, and it may launch the fixed `reorient-worker` for coordinator-approved `launchReorientWorker`. It does not auto-launch Imagination/modeling/verification, accept findings, promote evidence, edit implementation code, or keep going after reviewable semantic output.
|
||||
- Pre-compaction checkpoint intervention is now landed. On token-count events for loaded Epiphany threads, when current context usage reaches 80% of the active auto-compact/context limit, the harness steers the active turn once with a CRRC checkpoint directive so the agent banks working context before compaction/reorientation. Pressure ignores cumulative token spend; cumulative-only telemetry reports unknown instead of yelling. A successful steer now latches a turn-scoped compact handoff that is consumed at clean turn completion, preventing the brake from decaying into another implementation turn. This is still bounded steering plus compaction handoff, not automatic semantic acceptance, a broad scheduler, or implementation continuation.
|
||||
- The old Python dogfood/live-specialist runners were cut because they encoded the obsolete completion path. The replacement must be native Rust/CultCache/CultNet and complete heartbeat-owned runtime-spine job results with auditable artifacts.
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ the projection store contains one terrain report, one profile, all eight
|
|||
standing role projections, a rendered personality distiller packet plus prompt,
|
||||
and a rendered role-specific memory distiller packet plus prompt. It also
|
||||
proves `epiphany-repo-birth-runner --mode plan` turns missing birth packets into
|
||||
startup-only Codex execution artifacts and accept commands without creating
|
||||
startup-only typed runtime execution artifacts and accept commands without creating
|
||||
heartbeat participants.
|
||||
|
||||
## Next Implementation Step
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -139,7 +139,7 @@ accepted_design:
|
|||
- Native `epiphany-repo-personality` is the first startup-personality scout and birth-only distiller packet renderer: it deterministically inventories local git repos, scores repo terrain/history/state surfaces, writes typed CultCache terrain/profile/role-projection MessagePack stores, emits JSON/Markdown inspection glass, and renders a Repo Personality Distiller specialist prompt packet for Self-reviewed first role-memory petitions. The distiller must run only when a repo/swarm has no accepted personality initialization; after that, personality drift belongs to heartbeat, mood, rumination, sleep consolidation, lived evidence, and reviewed `selfPatch`. Startup routing now checks accepted initialization records, generates missing startup-only packets only once, applies reviewed birth memory and heartbeat physiology seeds, and leaves later drift alone. Birth specialists are not heartbeat lanes; heartbeat receives only accepted seed pressure.
|
||||
- Repo memory initialization is a separate birth-only organ from repo personality initialization. `epiphany-repo-personality memory-packet` uses the typed terrain/profile/projection store plus bounded source excerpts from doctrine, docs, state, research, contracts, verification, runtime, and code surfaces to render a Repo Memory Distiller packet with one mission-specific brief for Self, Face, Imagination, Eyes, Body, Hands, Soul, and Life. It petitions Self with role-specific Ghostlight-shaped memory candidates; it does not mutate memory, set objectives, copy raw dumps, or reset living memory after startup.
|
||||
- Repo trajectory initialization is now its own birth-only organ. `epiphany-repo-personality` derives a typed `repo_trajectory_report` from early-history, recent-history, doctrine/content excerpts, and deterministic theme scoring, and `trajectory-packet` renders a Repo Trajectory Distiller prompt/packet so Self can review historical direction, self-image, implicit goals, anti-goals, and role-local trajectory pressure before the swarm wakes.
|
||||
- The first repo birth startup valve is native: `epiphany-repo-personality startup` checks a typed CultCache init store for accepted `repo-trajectory`, `repo-personality`, and `repo-memory` records, generates missing birth packets for Self review, marks them as birth-only startup-runner work with no heartbeat participant, and stops with `reviewInitializationPackets`; `epiphany-repo-birth-runner` now turns those packets into startup-only Codex exec prompts, output schemas, logs, result files, and exact accept commands; `accept-init` can review/apply role `selfPatch` candidates from a distiller result through `state/agents.msgpack`, stamp `repo-personality` canonical Ghostlight trait bundles from deterministic role projections, seed `repo-personality` heartbeat physiology through `state/agent-heartbeats.msgpack`, record reviewed packet acceptance as `epiphany.repo_initialization_record.v0`, and later startup returns `continueStartup` without rerunning the birth rites. Aquarium review UI is still the next routing gap.
|
||||
- The first repo birth startup valve is native: `epiphany-repo-personality startup` checks a typed CultCache init store for accepted `repo-trajectory`, `repo-personality`, and `repo-memory` records, generates missing birth packets for Self review, marks them as birth-only startup-runner work with no heartbeat participant, and stops with `reviewInitializationPackets`; `epiphany-repo-birth-runner` now turns those packets into startup-only prompts, output schemas, typed OpenAI model-request documents, runtime-spine receipts, result files, and exact accept commands; `codex exec` survives only as explicit `--executor codex-exec` fallback; `accept-init` can review/apply role `selfPatch` candidates from a distiller result through `state/agents.msgpack`, stamp `repo-personality` canonical Ghostlight trait bundles from deterministic role projections, seed `repo-personality` heartbeat physiology through `state/agent-heartbeats.msgpack`, record reviewed packet acceptance as `epiphany.repo_initialization_record.v0`, and later startup returns `continueStartup` without rerunning the birth rites. Aquarium review UI is still the next routing gap.
|
||||
- Limited native CRRC automation is now wired only at turn-complete safe boundaries: it can submit a compact operation when the read-only coordinator recommends `compactRehydrateReorient` or when a successful pre-compaction checkpoint intervention left a pending compact handoff for the completed turn, and it can launch the fixed `reorient-worker` when the coordinator recommends `launchReorientWorker`. It does not auto-launch modeling or verification, auto-accept findings, promote evidence, edit implementation code, or continue after reviewable semantic results; the rite may carry the stretcher, not declare the patient healed.
|
||||
- Pre-compaction checkpoint intervention is now wired at token-count boundaries: when a loaded Epiphany thread crosses the existing `shouldPrepareCompaction` pressure threshold during an active turn, the harness steers the active agent once for that turn with a bounded CRRC directive to bank scratch/checkpoint/map/evidence before compaction/reorientation. A successful steer marks a turn-scoped pending compact handoff consumed at clean turn completion; this is banking the fire before the dark, not semantic acceptance, broad scheduling, or automatic implementation continuation.
|
||||
- Native `epiphany-mvp-status` is the first dogfood operator view: it starts or reads a thread through app-server and renders scene, planning, pressure, reorient, jobs, role result read-backs, reorient result, CRRC recommendation, heartbeat, Face bubbles, and MVP role lanes as text or machine output without becoming a state store. The old Python status module has been cut; native Rust/CultCache/CultNet surfaces are the product path.
|
||||
|
|
@ -231,7 +231,7 @@ landed_baseline:
|
|||
- Native repo personality scout/birth-distiller packet slice: `epiphany-repo-personality` emits typed terrain reports, repo personality profiles, and all eight role personality projections from deterministic repo inventory and git-history scoring, then renders a birth-only Repo Personality Distiller specialist packet and prompt for review-gated initial personality quirk petitions; `epiphany-repo-personality-smoke` proves scout/project/agent-packet/status over tiny local repos.
|
||||
- Native repo trajectory scout/birth-distiller packet slice: `epiphany-repo-personality` now also emits typed `repo_trajectory_report` documents from early-history, recent-history, doctrine/content excerpts, and deterministic theme scoring, then renders a birth-only Repo Trajectory Distiller packet so newborn swarms can inherit historical direction, self-image, implicit goals, and anti-goals without confusing those with active objectives.
|
||||
- Native repo memory scout/birth-distiller packet slice: `epiphany-repo-personality memory-packet` renders a separate Repo Memory Distiller prompt/packet with bounded source excerpts and role-specific distiller briefs, so a newborn swarm can be prefilled with mission-relevant doctrine, maps, research, implementation habits, verification practice, and continuity rules without confusing memory with personality.
|
||||
- Native repo birth startup valve slice: `epiphany-repo-personality startup`, `epiphany-repo-birth-runner`, and `accept-init` make trajectory/personality/memory birth packet generation, startup-only execution, and acceptance one-shot and review-gated through typed initialization records; generated packets advertise startup-runner ownership rather than heartbeat-lane identity; the birth runner has plan/run modes over `codex exec` and writes prompts, schemas, logs, result files, and accept commands; `accept-init` processes distiller result `selfPatch` candidates through the native agent-memory review/apply path, stamps reviewed `repo-personality` canonical trait bundles through the native agent-memory store, and applies reviewed `repo-personality` heartbeat seeds through the typed heartbeat store; smoke proves first startup emits packets, birth runner plan artifacts exist, canonical trait bundles replace newborn baseline placeholders, heartbeat physiology mutates, reviewed memory output mutates a copied role memory store, and post-accept startup continues without regeneration.
|
||||
- Native repo birth startup valve slice: `epiphany-repo-personality startup`, `epiphany-repo-birth-runner`, and `accept-init` make trajectory/personality/memory birth packet generation, startup-only execution, and acceptance one-shot and review-gated through typed initialization records; generated packets advertise startup-runner ownership rather than heartbeat-lane identity; the birth runner has plan/run modes over typed OpenAI runtime model requests by default, writes prompts, schemas, runtime requests/receipts, logs, result files, and accept commands, and keeps `codex exec` as explicit `--executor codex-exec` fallback; `accept-init` processes distiller result `selfPatch` candidates through the native agent-memory review/apply path, stamps reviewed `repo-personality` canonical trait bundles through the native agent-memory store, and applies reviewed `repo-personality` heartbeat seeds through the typed heartbeat store; smoke proves first startup emits packets, birth runner plan artifacts exist, canonical trait bundles replace newborn baseline placeholders, heartbeat physiology mutates, reviewed memory output mutates a copied role memory store, and post-accept startup continues without regeneration.
|
||||
guardrails:
|
||||
- Use focused Rust app-server mapper/handler tests plus matching native `epiphany-core` smoke binaries before modifying update, distill, propose, promote, read-projection, or notification behavior. The old Python smoke shims have been cut.
|
||||
- Use `cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-phase6-scene-smoke` before modifying scene projection behavior.
|
||||
|
|
@ -279,5 +279,5 @@ rejected_paths:
|
|||
reason: making Epiphany cleaner inside Codex still preserves the wrong organ; Codex must be starved down to OpenAI subscription auth/model transport, while Epiphany data and subsystem communication move to typed CultCache/CultNet.
|
||||
|
||||
current_status:
|
||||
summary: The Codex starvation pass has five typing receipts, eighteen processor cuts, a real physical extraction in `epiphany-codex-bridge`, a source-grounded Codex auth spine inventory, the native `epiphany-openai-adapter` document boundary, a buildable Codex auth/model wrapper in `epiphany-openai-codex-spine`, the first native runtime caller in `epiphany-openai-runtime`, native heartbeat launch planning/job opening in `epiphany-core`, native state-update contract/validation/application in `epiphany-core`, and the route-facing mutation service in `epiphany-codex-bridge`. The bridge owns Codex-facing Epiphany adapters for scene, jobs, context/planning/graph query, retrieval/indexing projection, result projection, runtime-spine result adaptation, launch/prompt doctrine, mutation document mechanics, mutation service update/promote/role-accept/reorient-accept/role-launch/reorient-launch/job-launch/job-interrupt application, live-state projection, pressure/pre-compaction rendering, freshness/reorientation projection, CRRC/coordinator/role-board projection, and acceptance/evidence signal helpers. Vendored app-server now retains only `epiphany_invalidation.rs` as a root Epiphany module because watcher lifecycle is still app-server machinery; `codex_message_processor.rs` still contains route dispatch, JSON-RPC response shaping, watcher registration/snapshotting, and notification emission. The processor is about 10,339 lines, `epiphany_mutation_routes.rs` is about 691 lines after the route-load/notification helper cut and index projection move, `epiphany_state_helpers.rs` is down to a 19-line rollout-state loader, and `codex_thread.rs` is about 1,425 lines after state-update law moved native. `epiphany-openai-adapter` defines CultCache `DatabaseEntry` request/event/receipt/auth-status documents with no Codex app-server or generic JSON dependency. `epiphany-openai-codex-spine` projects `AuthManager` / `CodexAuth` into typed `EpiphanyOpenAiAdapterStatus`, maps typed `EpiphanyOpenAiModelRequest` into Codex API `ResponsesApiRequest`, resolves auth/provider through keeper Codex crates, opens HTTP Responses streams through `codex-api`, maps deltas / completion into typed stream events and receipts, and no longer directly depends on `codex-app-server-protocol` just to name auth mode. `epiphany-openai-runtime` writes OpenAI status/request/stream-event/receipt records plus runtime sessions/jobs/results/events into `epiphany-core::runtime_spine_cache`, and its smoke proves the route without network. `epiphany-core::plan_runtime_spine_heartbeat_launch` validates launch contracts/conflicts and projects durable job bindings/runtime links; `epiphany-core::open_runtime_spine_heartbeat_job` owns the initialize-runtime/ensure-session/create-job sequence; `epiphany-core::EpiphanyStateUpdate`, `epiphany_state_update_validation_errors`, and `apply_epiphany_state_update` own state-update document shape and mutation law while `codex-core` calls them for compatibility. Vendored `CodexThread::epiphany_launch_job` is now mostly revision check, persistence validation, and rollout/session writeback for the compatibility route. `epiphany-runtime-spine` advertises OpenAI adapter status/model-request/stream-event/receipt CultNet schemas and mutation contracts, so the native caller now has a real storage route instead of only JSON-RPC. MCP itself is correctly JSON at its protocol edge; the target is typed Epiphany intent/result/receipt wrappers around MCP, not replacing MCP JSON. The current OpenAI spine still pulls `codex-api` and a large transport dependency stack, so its next cut is dependency starvation after live callers move.
|
||||
next_action: Move live model-turn callers onto `epiphany-openai-runtime` or the same native CultNet contract, then shrink the surviving Codex dependency surface to credential refresh, provider/model routing, and the smallest OpenAI Responses call required for Codex subscription compatibility.
|
||||
summary: The Codex starvation pass has five typing receipts, eighteen processor cuts, a real physical extraction in `epiphany-codex-bridge`, a source-grounded Codex auth spine inventory, the native `epiphany-openai-adapter` document boundary, a buildable Codex auth/model wrapper in `epiphany-openai-codex-spine`, the first native runtime caller in `epiphany-openai-runtime`, native heartbeat launch planning/job opening in `epiphany-core`, native state-update contract/validation/application in `epiphany-core`, and the route-facing mutation service in `epiphany-codex-bridge`. The bridge owns Codex-facing Epiphany adapters for scene, jobs, context/planning/graph query, retrieval/indexing projection, result projection, runtime-spine result adaptation, launch/prompt doctrine, mutation document mechanics, mutation service update/promote/role-accept/reorient-accept/role-launch/reorient-launch/job-launch/job-interrupt application, live-state projection, pressure/pre-compaction rendering, freshness/reorientation projection, CRRC/coordinator/role-board projection, and acceptance/evidence signal helpers. Vendored app-server now retains only `epiphany_invalidation.rs` as a root Epiphany module because watcher lifecycle is still app-server machinery; `codex_message_processor.rs` still contains route dispatch, JSON-RPC response shaping, watcher registration/snapshotting, and notification emission. The processor is about 10,339 lines, `epiphany_mutation_routes.rs` is about 691 lines after the route-load/notification helper cut and index projection move, `epiphany_state_helpers.rs` is down to a 19-line rollout-state loader, and `codex_thread.rs` is about 1,425 lines after state-update law moved native. `epiphany-openai-adapter` defines CultCache `DatabaseEntry` request/event/receipt/auth-status documents with no Codex app-server or generic JSON dependency. `epiphany-openai-codex-spine` projects `AuthManager` / `CodexAuth` into typed `EpiphanyOpenAiAdapterStatus`, maps typed `EpiphanyOpenAiModelRequest` into Codex API `ResponsesApiRequest`, resolves auth/provider through keeper Codex crates, opens HTTP Responses streams through `codex-api`, maps deltas / completion into typed stream events and receipts, and no longer directly depends on `codex-app-server-protocol` just to name auth mode. `epiphany-openai-runtime` writes OpenAI status/request/stream-event/receipt records plus runtime sessions/jobs/results/events into `epiphany-core::runtime_spine_cache`, can emit the assistant text artifact for legacy result consumers, and is now the default executor for startup-only repo birth specialists; `codex exec` survives there only as explicit fallback. `epiphany-core::plan_runtime_spine_heartbeat_launch` validates launch contracts/conflicts and projects durable job bindings/runtime links; `epiphany-core::open_runtime_spine_heartbeat_job` owns the initialize-runtime/ensure-session/create-job sequence; `epiphany-core::EpiphanyStateUpdate`, `epiphany_state_update_validation_errors`, and `apply_epiphany_state_update` own state-update document shape and mutation law while `codex-core` calls them for compatibility. Vendored `CodexThread::epiphany_launch_job` is now mostly revision check, persistence validation, and rollout/session writeback for the compatibility route. `epiphany-runtime-spine` advertises OpenAI adapter status/model-request/stream-event/receipt CultNet schemas and mutation contracts, so the native caller now has a real storage route instead of only JSON-RPC. MCP itself is correctly JSON at its protocol edge; the target is typed Epiphany intent/result/receipt wrappers around MCP, not replacing MCP JSON. The current OpenAI spine still pulls `codex-api` and a large transport dependency stack, so its next cut is dependency starvation after more live callers move.
|
||||
next_action: Move the remaining live model-turn callers, especially heartbeat/specialist execution, onto `epiphany-openai-runtime` or the same native CultNet contract, then shrink the surviving Codex dependency surface to credential refresh, provider/model routing, and the smallest OpenAI Responses call required for Codex subscription compatibility.
|
||||
|
|
|
|||
1
vendor/codex/codex-rs/Cargo.lock
generated
vendored
1
vendor/codex/codex-rs/Cargo.lock
generated
vendored
|
|
@ -5272,7 +5272,6 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"codex-api",
|
||||
"codex-app-server-protocol",
|
||||
"codex-login",
|
||||
"codex-model-provider",
|
||||
"codex-model-provider-info",
|
||||
|
|
|
|||
Reference in a new issue