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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 15 additions & 6 deletions .agent/skills/analyze_session/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ allowed_tools:
# Analyze Session

Use the smallest authoritative evidence path that answers the question. For the
active session, structured observation is primary: call `introspect` once with
`facet=overview`, `depth=diagnostic`, and `horizon=recent`, then call `reflect`
at most once with `topic=overview`, `facet=overview`, `depth=diagnostic`,
`horizon=session`, and a concrete causal question. These overview calls are
composite snapshots; do not scan individual facets unless their result identifies
a specific evidence gap.
active session, start with a summary `introspect` overview (`hint` for a quick
check). Use `reflect` with a concrete causal question only when persisted
evidence is needed; its summary overview is the default. Reuse observations
already obtained within the relevant scope. Expand depth or facets only for a
specific evidence gap or an explicitly requested deep audit; discover the full
tool contract before using fields absent from the resident schema.

Use `astra journal digest` for a named past/offline session, exact aggregate
metrics, durable-event ordering, or a concrete gap reported by structured
Expand Down Expand Up @@ -84,6 +84,10 @@ For structured observation, distinguish live `introspect` facts from persisted
`reflect` evidence and cite that boundary in the answer. Do not turn the user's
request for a retrospective into an exhaustive telemetry inventory.

Snapshots exclude later calls, and completed-turn aggregates can exclude the
ongoing turn. State the observed scope; a zero in that scope does not prove
absence elsewhere. Account separately for diagnostic calls made afterward.

When a digest is required, trust its stable schema:

Stable schema: `schema_version = "astra-journal-digest-v2"` from
Expand Down Expand Up @@ -167,6 +171,11 @@ Use only when the digest does not answer the question.

## Output Contract

For routine checks, give a brief conclusion, supporting observation, and any
remaining uncertainty. Use the detailed format below only for a substantive
incident; include digest metrics and executable provenance only when relevant
and actually obtained. Do not acquire extra evidence just to fill a template.

```text
Findings:
- <highest-impact diagnosis with digest evidence>
Expand Down
21 changes: 15 additions & 6 deletions .claude/skills/analyze_session/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ allowed_tools:
# Analyze Session

Use the smallest authoritative evidence path that answers the question. For the
active session, structured observation is primary: call `introspect` once with
`facet=overview`, `depth=diagnostic`, and `horizon=recent`, then call `reflect`
at most once with `topic=overview`, `facet=overview`, `depth=diagnostic`,
`horizon=session`, and a concrete causal question. These overview calls are
composite snapshots; do not scan individual facets unless their result identifies
a specific evidence gap.
active session, start with a summary `introspect` overview (`hint` for a quick
check). Use `reflect` with a concrete causal question only when persisted
evidence is needed; its summary overview is the default. Reuse observations
already obtained within the relevant scope. Expand depth or facets only for a
specific evidence gap or an explicitly requested deep audit; discover the full
tool contract before using fields absent from the resident schema.

Use `astra journal digest` for a named past/offline session, exact aggregate
metrics, durable-event ordering, or a concrete gap reported by structured
Expand Down Expand Up @@ -84,6 +84,10 @@ For structured observation, distinguish live `introspect` facts from persisted
`reflect` evidence and cite that boundary in the answer. Do not turn the user's
request for a retrospective into an exhaustive telemetry inventory.

Snapshots exclude later calls, and completed-turn aggregates can exclude the
ongoing turn. State the observed scope; a zero in that scope does not prove
absence elsewhere. Account separately for diagnostic calls made afterward.

When a digest is required, trust its stable schema:

Stable schema: `schema_version = "astra-journal-digest-v2"` from
Expand Down Expand Up @@ -167,6 +171,11 @@ Use only when the digest does not answer the question.

## Output Contract

For routine checks, give a brief conclusion, supporting observation, and any
remaining uncertainty. Use the detailed format below only for a substantive
incident; include digest metrics and executable provenance only when relevant
and actually obtained. Do not acquire extra evidence just to fill a template.

```text
Findings:
- <highest-impact diagnosis with digest evidence>
Expand Down
7 changes: 4 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,10 @@ ASTRA_RUNTIME_ROOT_SECRET=your-runtime-root-secret-min-32-characters-change-me
# tracked requests from silently becoming untracked tool calls without adding
# judge latency to the ordinary primary path. `capacity_aware` keeps this
# correctness boundary enabled while provider admission accounts for its
# quota; unrelated optional judges remain capacity-gated. Use `disabled` to
# remove every auxiliary call and accept typed-topology fallback, or `always`
# to ignore capacity gating for all eligible auxiliary features.
# quota; unrelated optional judges remain capacity-gated. Under Auto,
# `disabled` makes semantic admission unavailable and therefore fails closed;
# clients that deliberately omit classification must request FixedDefault.
# Use `always` to ignore capacity gating for all eligible auxiliary features.
#ASTRA_AUX_LLM_POLICY=capacity_aware

# ── Memoria (Memory Service) ──────────────────────────────────────────────
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ jobs:
-p astra-logging -p astra-sync-protocol
--profile ci
- name: Test server e2e-hooks (selective)
env:
RUST_MIN_STACK: "16777216"
run: >-
cargo nextest run --manifest-path Cargo.toml -p astra-runtime
--features e2e-hooks --profile ci
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@ test-server-edge:
@CARGO_INCREMENTAL=0 $(CARGO) test $(CARGO_MANIFEST_FLAG) $(API_SHELL_PKG) --lib edge_bound_offline_or_unknown_status_blocks_without_dispatch
@CARGO_INCREMENTAL=0 $(CARGO) test $(CARGO_MANIFEST_FLAG) $(API_SHELL_PKG) --lib edge_dispatch_without_result_reports_transport_disconnected
@CARGO_INCREMENTAL=0 $(CARGO) test $(CARGO_MANIFEST_FLAG) -p astra-runtime --test edge_ws_e2e edge_ws_relay_strips_legacy_boundary_and_preserves_inflight_dispatch
@CARGO_INCREMENTAL=0 $(CARGO) test $(CARGO_MANIFEST_FLAG) -p astra-runtime --test web_agent_e2e --features e2e-hooks web_agent_dynamic_spawn_inherits_edge_workspace_binding
@RUST_MIN_STACK=$${RUST_MIN_STACK:-16777216} CARGO_INCREMENTAL=0 $(CARGO) test $(CARGO_MANIFEST_FLAG) -p astra-runtime --test web_agent_e2e --features e2e-hooks web_agent_dynamic_spawn_inherits_edge_workspace_binding
@CARGO_INCREMENTAL=0 $(CARGO) test $(CARGO_MANIFEST_FLAG) -p astra-runtime --test web_agent_e2e --features e2e-hooks edge_executor_offline_child_returns_actionable_wait_to_structured_parent
@cd web && npm test -- --run \
__tests__/app/edges-status-route.test.ts \
Expand Down Expand Up @@ -1139,7 +1139,7 @@ test-workspace: sweep
.PHONY: test-runtime-e2e-hooks
test-runtime-e2e-hooks: sweep
@echo "Running astra-runtime tests with feature e2e-hooks (nextest profile=$(NEXTEST_OFFLINE_PROFILE))..."
@CARGO_INCREMENTAL=0 cargo nextest run $(CARGO_MANIFEST_FLAG) $(API_SHELL_PKG) \
@RUST_MIN_STACK=$${RUST_MIN_STACK:-16777216} CARGO_INCREMENTAL=0 cargo nextest run $(CARGO_MANIFEST_FLAG) $(API_SHELL_PKG) \
--features e2e-hooks $(NEXTEST_OFFLINE_FLAGS)

# Ignored tests: opt-in via env vars (see `make test-online`). Enable with:
Expand Down
15 changes: 5 additions & 10 deletions crates/astra-cli/src/cli/app_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,14 @@ async fn run_turn(
.or(developer_instructions)
.or(ctx.system_prompt.clone());
let continuation = app_server_continuation(&thread_id);
let activated_deferred_tool_names = continuation
let deferred_tool_activations = continuation
.as_ref()
.map(|continuation| continuation.activated_deferred_tool_names.clone())
.map(|continuation| continuation.deferred_tool_activations.clone())
.unwrap_or_default();
let continuation_messages = continuation.map(|continuation| continuation.messages);
let turn_options = crate::cli::turn::turn_facade::BasicCliTurnOptions {
pre_loaded_messages: continuation_messages,
activated_deferred_tool_names,
deferred_tool_activations,
append_system_prompt,
cancel_token: Some(cancel),
approval_request_tx: Some(approval_tx.clone()),
Expand Down Expand Up @@ -1268,7 +1268,6 @@ mod tests {
let mut result = crate::tests::stub_stream_result("done");
result.session_id = Some(session_id.clone());
result.tools_used = vec!["github".to_string()];
result.activated_deferred_tool_names = vec!["github".to_string()];
result.final_messages = vec![
serde_json::json!({"role": "user", "content": "list pull requests"}),
serde_json::json!({"role": "assistant", "content": "done"}),
Expand All @@ -1289,15 +1288,11 @@ mod tests {
let csl = crate::cli::session::session_continuation::load_csl_continuation(&session_id)
.expect("exact CSL projection must parse")
.expect("exact CSL projection must exist");
assert_eq!(csl.activated_deferred_tool_names, vec!["github"]);
assert!(csl.deferred_tool_activations.is_empty());

let continuation =
app_server_continuation(&session_id).expect("next app-server turn continuation");
assert_eq!(
continuation.activated_deferred_tool_names,
vec!["github"],
"a long-lived app-server must not require tool_search again on every visible turn"
);
assert!(continuation.deferred_tool_activations.is_empty());
assert_eq!(
continuation
.messages
Expand Down
15 changes: 9 additions & 6 deletions crates/astra-cli/src/cli/auth_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,14 +642,17 @@ pub(crate) async fn discover_login_website(
}
}

fn login_website_host_is_loopback(host: &str) -> bool {
host.eq_ignore_ascii_case("localhost")
|| host
.trim_matches(['[', ']'])
.parse::<std::net::IpAddr>()
.is_ok_and(|ip| ip.is_loopback())
}

fn validate_login_website(value: &str) -> Result<url::Url, String> {
let url = url::Url::parse(value).map_err(|_| "Invalid Server login URL")?;
let loopback = url.host_str().is_some_and(|h| {
h == "localhost"
|| h.trim_matches(['[', ']'])
.parse::<std::net::IpAddr>()
.is_ok_and(|ip| ip.is_loopback())
});
let loopback = url.host_str().is_some_and(login_website_host_is_loopback);
if !matches!(url.scheme(), "http" | "https")
|| url.host_str().is_none()
|| !url.username().is_empty()
Expand Down
18 changes: 15 additions & 3 deletions crates/astra-cli/src/cli/auth_flow/browser_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,23 @@ pub(super) async fn redeem(
port: u16,
state: &str,
) -> Result<String, String> {
super::validate_login_website(website)?;
let client = reqwest::Client::builder()
let website_url = super::validate_login_website(website)?;
let builder = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.connect_timeout(Duration::from_secs(5))
.timeout(Duration::from_secs(15))
.timeout(Duration::from_secs(15));
// A proxy cannot route a process-local test/dev website. Match the
// login-URL security boundary: local HTTP endpoints are direct, while
// remote HTTPS endpoints retain the configured proxy behavior.
let builder = if website_url
.host_str()
.is_some_and(super::login_website_host_is_loopback)
{
builder.no_proxy()
} else {
builder
};
let client = builder
.build()
.map_err(|_| "Could not initialize login code exchange")?;
let mut response = client
Expand Down
5 changes: 3 additions & 2 deletions crates/astra-cli/src/cli/chat_stream/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,8 @@ pub(crate) struct ChatTurnParams<'a> {
pub(crate) cli_context: Option<&'a CliContext>,

pub(crate) recent_tools: &'a [String],
pub(crate) activated_deferred_tool_names: Option<&'a mut Vec<String>>,
pub(crate) deferred_tool_activations:
Option<&'a mut Vec<astra_turn_types::DeferredToolActivation>>,
pub(crate) tool_health_entries: &'a [ToolHealthEntry],
pub(crate) resume_restricted_tools: &'a [String],
/// P6 seam: cross-session lessons loaded once at session bootstrap.
Expand Down Expand Up @@ -768,7 +769,7 @@ impl<'a> ChatTurnParams<'a> {
cli_context: ctx.cli_context,

recent_tools: &[],
activated_deferred_tool_names: None,
deferred_tool_activations: None,
tool_health_entries: &[],
resume_restricted_tools: &[],
session_lessons: &[],
Expand Down
Loading
Loading