fix(hive-claude): report Progress.created on first-turn overflow, compacted only when compact succeeded
This commit is contained in:
parent
216eb8ae9b
commit
dd51d02bc2
1 changed files with 32 additions and 16 deletions
|
|
@ -66,19 +66,25 @@ impl<P: CompactionPolicy> InfiniteSession<P> {
|
||||||
/// [`Error::PromptTooLong`]; rate-limit / auth / hard failures propagate
|
/// [`Error::PromptTooLong`]; rate-limit / auth / hard failures propagate
|
||||||
/// unchanged for the caller to handle.
|
/// unchanged for the caller to handle.
|
||||||
pub async fn run(&self, config: &Config, prompt: &str, sink: &impl Sink) -> Result<Progress> {
|
pub async fn run(&self, config: &Config, prompt: &str, sink: &impl Sink) -> Result<Progress> {
|
||||||
|
// Resolve resume-vs-create once, up front, so `created` reflects the
|
||||||
|
// session state at the START of the run: the reactive-retry path can't
|
||||||
|
// read it from the retry (the session exists by then).
|
||||||
|
let existed = self.store.find_by_title(&self.name).is_some();
|
||||||
let meter = TelemetrySink::new(sink);
|
let meter = TelemetrySink::new(sink);
|
||||||
let created = match self.attempt(config, prompt, &meter).await {
|
let created = match self.attempt(config, prompt, &meter, existed).await {
|
||||||
Ok(created) => created,
|
Ok(created) => created,
|
||||||
Err(Error::PromptTooLong) => {
|
Err(Error::PromptTooLong) => {
|
||||||
// The session is already past the window — no turn can run on
|
// The session is already past the window — no turn can run on
|
||||||
// it and the detail is gone (no checkpoint possible). Compact,
|
// it and the detail is gone (no checkpoint possible). Compact,
|
||||||
// then retry the same prompt once; the retry is the answering
|
// then retry the same prompt once; the retry is the answering
|
||||||
// turn, so its telemetry is what we report.
|
// turn, so its telemetry is what we report. The failed attempt
|
||||||
|
// created/resumed the session, so the retry resumes it, and
|
||||||
|
// `created` still reflects the pre-run state.
|
||||||
self.compact(config, sink).await?;
|
self.compact(config, sink).await?;
|
||||||
let retry = TelemetrySink::new(sink);
|
let retry = TelemetrySink::new(sink);
|
||||||
let created = self.attempt(config, prompt, &retry).await?;
|
self.attempt(config, prompt, &retry, true).await?;
|
||||||
return Ok(Progress {
|
return Ok(Progress {
|
||||||
created,
|
created: !existed,
|
||||||
compacted: true,
|
compacted: true,
|
||||||
telemetry: retry.snapshot(),
|
telemetry: retry.snapshot(),
|
||||||
});
|
});
|
||||||
|
|
@ -91,12 +97,15 @@ impl<P: CompactionPolicy> InfiniteSession<P> {
|
||||||
// says it's due, checkpoint (best-effort) then compact.
|
// says it's due, checkpoint (best-effort) then compact.
|
||||||
if self.policy.should_compact(telemetry.usage()) {
|
if self.policy.should_compact(telemetry.usage()) {
|
||||||
if let Some(checkpoint) = self.policy.checkpoint_prompt() {
|
if let Some(checkpoint) = self.policy.checkpoint_prompt() {
|
||||||
let _ = self.attempt(config, checkpoint, sink).await;
|
let _ = self.attempt(config, checkpoint, sink, true).await;
|
||||||
}
|
}
|
||||||
let _ = self.compact(config, sink).await;
|
// Only claim a compaction if it actually succeeded — the flag feeds
|
||||||
|
// stats + the auto-reset watermark, so a failed best-effort
|
||||||
|
// `/compact` must not report that the context shrank.
|
||||||
|
let compacted = self.compact(config, sink).await.is_ok();
|
||||||
return Ok(Progress {
|
return Ok(Progress {
|
||||||
created,
|
created,
|
||||||
compacted: true,
|
compacted,
|
||||||
telemetry,
|
telemetry,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -121,22 +130,29 @@ impl<P: CompactionPolicy> InfiniteSession<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One resume-or-create turn. Uses the store to pick resume vs. create up
|
/// One resume-or-create turn. `existed` is the caller's up-front
|
||||||
/// front (avoiding a wasted resume-miss spawn), and still self-heals if the
|
/// resume-vs-create decision (whether the titled session was on disk before
|
||||||
/// backing file vanished between the check and the run. Returns whether a
|
/// the run) — passing it in rather than re-checking keeps `created`
|
||||||
/// fresh session was created.
|
/// reporting consistent across the reactive-retry path. Still self-heals if
|
||||||
async fn attempt(&self, config: &Config, prompt: &str, sink: &impl Sink) -> Result<bool> {
|
/// the backing file vanished between the check and the run. Returns whether
|
||||||
let exists = self.store.find_by_title(&self.name).is_some();
|
/// a fresh session was created.
|
||||||
let attach = if exists {
|
async fn attempt(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
prompt: &str,
|
||||||
|
sink: &impl Sink,
|
||||||
|
existed: bool,
|
||||||
|
) -> Result<bool> {
|
||||||
|
let attach = if existed {
|
||||||
Attach::Resume(self.name.clone())
|
Attach::Resume(self.name.clone())
|
||||||
} else {
|
} else {
|
||||||
Attach::Create(self.name.clone())
|
Attach::Create(self.name.clone())
|
||||||
};
|
};
|
||||||
match Claude::run(config, &attach, prompt, sink).await {
|
match Claude::run(config, &attach, prompt, sink).await {
|
||||||
Ok(()) => Ok(!exists),
|
Ok(()) => Ok(!existed),
|
||||||
// We thought it existed but the resume missed (raced an archive) —
|
// We thought it existed but the resume missed (raced an archive) —
|
||||||
// self-heal by creating.
|
// self-heal by creating.
|
||||||
Err(Error::SessionNotFound) if exists => {
|
Err(Error::SessionNotFound) if existed => {
|
||||||
Claude::run(config, &Attach::Create(self.name.clone()), prompt, sink).await?;
|
Claude::run(config, &Attach::Create(self.name.clone()), prompt, sink).await?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue