Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pseq"
version = "0.0.3"
version = "0.0.4"
edition = "2024"
license = "MIT"
description = "Simple prompt and command sequencer for CLI agent automation."
Expand Down
2 changes: 1 addition & 1 deletion npm/pseq/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@s-brez/pseq",
"version": "0.0.3",
"version": "0.0.4",
"description": "Simple prompt and command sequencer for CLI agent automation.",
"main": "bin/pseq.js",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod runner;
pub mod sequence;
pub mod store;
mod trust;
mod turn_settings;
mod user_config;
pub mod yaml;

Expand Down
3 changes: 2 additions & 1 deletion src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ const SEQUENCE_ID_PREFIX: &str = "seq_";
const INCLUDE_PREFIX: &str = "pseq.fragment.";

pub use commands::{render, render_turns};
pub(crate) use engine::render_sequence_turns;
pub(crate) use engine::render_sequence_runtime_turns;
pub(crate) use load::load_current_sequence;
pub use types::{
RenderOptions, RenderOutput, RenderTurnsOptions, RenderedSequenceTurns, RenderedTurn,
RenderedTurnFragment, SavedRenderSummary,
};
pub(crate) use types::{RenderedRuntimeTurn, RenderedSequenceRuntimeTurns};
pub use validation::validate_saved_renders;
pub(crate) use variables::{load_variables, validate_variable_name};
47 changes: 42 additions & 5 deletions src/render/engine.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::BTreeMap;

use crate::error::AppError;
use crate::turn_settings;

use super::INCLUDE_PREFIX;
use super::fragments::resolve_render_fragment;
Expand Down Expand Up @@ -29,11 +30,7 @@ pub(crate) fn render_sequence_turns(
.map(|(index, fragment)| {
Ok(RenderedTurn {
index: index + 1,
fragment: RenderedTurnFragment {
id: fragment.id.clone(),
name: fragment.name.clone(),
path: fragment.path.clone(),
},
fragment: rendered_turn_fragment(fragment),
text: render_fragment_body(fragment, &sequence.catalog, variables)?,
})
})
Expand All @@ -47,6 +44,38 @@ pub(crate) fn render_sequence_turns(
})
}

pub(crate) fn render_sequence_runtime_turns(
sequence: &RenderSequence,
variables: &BTreeMap<String, String>,
) -> Result<RenderedSequenceRuntimeTurns, AppError> {
let turns = sequence
.fragments
.iter()
.enumerate()
.map(|(index, fragment)| {
let rendered_fragment = rendered_turn_fragment(fragment);
let settings = turn_settings::fragment_turn_settings(
fragment.pseq_metadata.as_ref(),
fragment.dotted_reasoning_effort.as_ref(),
&rendered_fragment,
)?;
Ok(RenderedRuntimeTurn {
index: index + 1,
fragment: rendered_fragment,
settings,
text: render_fragment_body(fragment, &sequence.catalog, variables)?,
})
})
.collect::<Result<Vec<_>, AppError>>()?;

Ok(RenderedSequenceRuntimeTurns {
id: sequence.id.clone(),
name: sequence.name.clone(),
path: sequence.path.clone(),
turns,
})
}

pub(super) fn render_text(
fragments: &[RenderFragment],
catalog: &[RenderFragment],
Expand Down Expand Up @@ -175,6 +204,14 @@ fn fragment_include_frame(fragment: &RenderFragment) -> FragmentIncludeFrame {
}
}

fn rendered_turn_fragment(fragment: &RenderFragment) -> RenderedTurnFragment {
RenderedTurnFragment {
id: fragment.id.clone(),
name: fragment.name.clone(),
path: fragment.path.clone(),
}
}

fn fragment_label(fragment: &RenderFragment) -> String {
format!("{} ({})", fragment.name, fragment.path)
}
Expand Down
2 changes: 2 additions & 0 deletions src/render/fragments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ fn parse_render_fragment(
id: metadata.id,
name: metadata.name,
path,
pseq_metadata: metadata.pseq,
dotted_reasoning_effort: metadata.dotted_reasoning_effort,
body: body.to_owned(),
})
}
8 changes: 8 additions & 0 deletions src/render/model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize};

use crate::yaml;

#[derive(Debug)]
pub(crate) struct RenderSequence {
pub(super) id: String,
Expand All @@ -14,6 +16,8 @@ pub(super) struct RenderFragment {
pub(super) id: String,
pub(super) name: String,
pub(super) path: String,
pub(super) pseq_metadata: Option<yaml::Value>,
pub(super) dotted_reasoning_effort: Option<yaml::Value>,
pub(super) body: String,
}

Expand Down Expand Up @@ -52,6 +56,10 @@ pub(super) struct HistoricalSequenceRecord {
pub(super) struct RenderFragmentFrontmatter {
pub(super) id: String,
pub(super) name: String,
#[serde(default)]
pub(super) pseq: Option<yaml::Value>,
#[serde(default, rename = "pseq.run.reasoning_effort")]
pub(super) dotted_reasoning_effort: Option<yaml::Value>,
}

#[derive(Debug)]
Expand Down
17 changes: 17 additions & 0 deletions src/render/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use serde::Serialize;
use std::path::Path;

use crate::commit::CommitMode;
use crate::turn_settings::TurnRuntimeSettings;

#[derive(Debug)]
pub struct RenderOptions<'a> {
Expand Down Expand Up @@ -61,6 +62,22 @@ pub struct RenderedTurnFragment {
pub path: String,
}

#[derive(Debug, Clone)]
pub(crate) struct RenderedSequenceRuntimeTurns {
pub(crate) id: String,
pub(crate) name: String,
pub(crate) path: String,
pub(crate) turns: Vec<RenderedRuntimeTurn>,
}

#[derive(Debug, Clone)]
pub(crate) struct RenderedRuntimeTurn {
pub(crate) index: usize,
pub(crate) fragment: RenderedTurnFragment,
pub(crate) settings: TurnRuntimeSettings,
pub(crate) text: String,
}

#[derive(Debug, Serialize)]
pub struct SavedRenderSummary {
pub id: String,
Expand Down
82 changes: 71 additions & 11 deletions src/run/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ use super::diagnostics::{
should_write_diagnostics, write_runner_failure_diagnostic, write_runner_retry_diagnostic,
write_turn_diagnostic,
};
use super::harnesses::{RunnerHarnessSession, prepare_runner_command};
use super::harnesses::{
HarnessTurnRequest, RunnerHarnessSession, prepare_runner_command,
runner_command_supports_turn_settings, validate_active_codex_turn_settings,
validate_command_turn_settings,
};
use super::model::{OutputMode, ProcessTermination, ProcessTurnOutput};
use super::options::{
RunSettings, feedback_variable, load_base_variables, load_feedback_seed, output_mode,
resolve_run_settings, resolve_runner, validate_options,
};
use super::types::*;
use crate::runner::ResolvedRunner;

pub fn run_sequence(
store_path: &Path,
Expand All @@ -37,12 +42,13 @@ pub fn run_sequence(
first_iteration_variables.insert(variable.clone(), previous_feedback.clone());
}
let first_sequence =
render::render_sequence_turns(&render_sequence, &first_iteration_variables)?;
render::render_sequence_runtime_turns(&render_sequence, &first_iteration_variables)?;
if options.feedback_from.is_some() && first_sequence.turns.is_empty() {
return Err(AppError::InvalidRunInvocation {
message: "feedback requires a sequence with at least one turn".to_owned(),
});
}
preflight_turn_settings_support(&first_sequence, &runner, &options)?;

let sequence_summary = RunSequenceSummary {
id: first_sequence.id.clone(),
Expand All @@ -69,7 +75,7 @@ pub fn run_sequence(
if let Some(variable) = &feedback_variable {
variables.insert(variable.clone(), previous_feedback.clone());
}
render::render_sequence_turns(&render_sequence, &variables)?
render::render_sequence_runtime_turns(&render_sequence, &variables)?
};

let mut iteration_feedback = None;
Expand Down Expand Up @@ -104,6 +110,8 @@ pub fn run_sequence(
RunAttemptRequest {
command: &command,
prompt: &turn.text,
fragment: &turn.fragment,
turn_settings: turn.settings,
output_mode,
max_captured_output: options.max_captured_output,
has_later_turn,
Expand Down Expand Up @@ -189,6 +197,54 @@ pub fn run_sequence(
))
}

fn preflight_turn_settings_support(
sequence: &render::RenderedSequenceRuntimeTurns,
runner: &ResolvedRunner,
options: &RunOptions<'_>,
) -> Result<(), AppError> {
let turns_per_iteration = sequence.turns.len();
let total_turns = turns_per_iteration * options.iterations;
let mut run_scope_codex_session = false;

for iteration in 1..=options.iterations {
let mut iteration_scope_codex_session = false;
for turn in &sequence.turns {
let global_turn_index = (iteration - 1) * turns_per_iteration + turn.index;
let scoped_turn_index = match options.session_scope {
SessionScope::Run => global_turn_index,
SessionScope::Iteration => turn.index,
};
let has_later_turn = match options.session_scope {
SessionScope::Run => global_turn_index < total_turns,
SessionScope::Iteration => turn.index < turns_per_iteration,
};
let command = runner.command_for_turn(scoped_turn_index);
let active_codex_session = match options.session_scope {
SessionScope::Run => run_scope_codex_session,
SessionScope::Iteration => iteration_scope_codex_session,
};

if active_codex_session {
validate_active_codex_turn_settings(turn.settings, &turn.fragment)?;
} else {
validate_command_turn_settings(command, turn.settings, &turn.fragment)?;
}

if !active_codex_session
&& has_later_turn
&& runner_command_supports_turn_settings(command)
{
match options.session_scope {
SessionScope::Run => run_scope_codex_session = true,
SessionScope::Iteration => iteration_scope_codex_session = true,
}
}
}
}

Ok(())
}

#[derive(Debug)]
struct RunAttemptRecord {
attempt: usize,
Expand All @@ -207,6 +263,8 @@ struct RetryDiagnosticContext {
struct RunAttemptRequest<'a> {
command: &'a [String],
prompt: &'a str,
fragment: &'a render::RenderedTurnFragment,
turn_settings: crate::turn_settings::TurnRuntimeSettings,
output_mode: OutputMode,
max_captured_output: usize,
has_later_turn: bool,
Expand All @@ -223,13 +281,15 @@ fn run_turn_attempts(
let mut attempts = Vec::new();

for attempt in 1..=max_attempts {
let (attempt_command, process) = runner_session.run_turn(
request.command,
request.prompt,
request.output_mode,
request.max_captured_output,
request.has_later_turn,
)?;
let (attempt_command, process) = runner_session.run_turn(HarnessTurnRequest {
argv: request.command,
prompt: request.prompt,
fragment: request.fragment,
settings: request.turn_settings,
output_mode: request.output_mode,
max_captured_output: request.max_captured_output,
needs_continuation: request.has_later_turn,
})?;
let retryable = is_retryable_runner_failure(&process);
let success = process.success;
attempts.push(RunAttemptRecord {
Expand Down Expand Up @@ -272,7 +332,7 @@ fn is_retryable_runner_failure(process: &ProcessTurnOutput) -> bool {
fn run_turn_output(
iterations: usize,
iteration: usize,
turn: &render::RenderedTurn,
turn: &render::RenderedRuntimeTurn,
command: &[String],
process: &ProcessTurnOutput,
include_output: bool,
Expand Down
Loading
Loading