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
6 changes: 3 additions & 3 deletions .repository-projection.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"projection": "deixic-code",
"projectionSchemaVersion": 1,
"sourceRepository": "dx-corp/mono",
"sourceSha": "883bd85c592bbfc7ac95992291f8b02e87011ecd",
"sourceSha": "06b74765cc5e7353134d868de4140e69fa634f38",
"destinationRepository": "dx-corp/code",
"priorProjectedBase": "6ede82720df7af1028a4791aeb49593e5c9c7bbc",
"priorProjectedBase": "85879e61077a7fd73f0dedcfd21e7c06e7f2f280",
"definitionDigest": "82936441c776e3e8edb5d215a75007ec9714a233f489d460075d79d5ef5ba32f",
"toolDigest": "45502ff0478e541d02f34cc39ac332935f8d3c0030fa221afe5bcc3b5e51f88e",
"contentDigest": "cd333a61496a0fd449c9b897ac7dec59e0302939c30d209e47898db02ae480d9",
"contentDigest": "65e63c3da4396f8ae48cd156df59c171580f0159ba3d1d943b9fe667879f9856",
"publicationEligible": true
}
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,44 @@ versioning when releases are cut.
and keep scheduled public runs inert so public publishing stays downstream of
the internal source-of-truth release.

## [0.10.94] - 2026-09-23

### Added

- Attach CLI to hosted Dex threads (#10258). <!-- maestro-release-note:1e5e3b1e46cc -->
- Use Dex orbit across terminal and desktop (#10250). <!-- maestro-release-note:e7c1ef11ba5f -->
- Refine Dex orb across desktop and terminal (#10264). <!-- maestro-release-note:beac37e7ecde -->

### Changed

- Expect budget terminal after spent fallback turn (#10259). <!-- maestro-release-note:cca477bb3998 -->
- Release v0.10.93 with Dex orbit (#10255). <!-- maestro-release-note:f72e07ee04fe -->
- Update dtolnay/rust-toolchain requirement to 02cb101ec7c40f2c49e1d9714d64511d8e1b74de (#10221). <!-- maestro-release-note:ce2d3f1528e5 -->
- Refresh bundled model catalog (#10254). <!-- maestro-release-note:70af0f581dfb -->
- Bump lucide-react in /products/maestro/desktop (#10224). <!-- maestro-release-note:35ec0f7ee3f3 -->
- Offload small verdict jobs to desktop (#10249). <!-- maestro-release-note:995d1685aa3a -->
- Require accepted source ref for execution workspace creation (#10245). <!-- maestro-release-note:1d300d831297 -->
- Borrow synthetic credential fixture for scan (#10238). <!-- maestro-release-note:4cd2a32d4187 -->
- Reject overflowing canary timeout values (#10237). <!-- maestro-release-note:2089726d64a1 -->
- Cover execution workspace admission fencing (#10232). <!-- maestro-release-note:89c215040d84 -->
- Create execution workspace on project task admission (#10229). <!-- maestro-release-note:e28e0907930e -->
- Enumerate bounded state-machine histories (#10228). <!-- maestro-release-note:017f1c873668 -->

### Fixed

- Repair provider request borrows (#10262). <!-- maestro-release-note:f7fa65426e74 -->
- Dispatch Maestro release without gh CLI (#10263). <!-- maestro-release-note:feaf22d453ab -->
- Keep incomplete provider usage unknown (#10261). <!-- maestro-release-note:be8d00b50511 -->
- Satisfy Clippy for thread attach (#10260). <!-- maestro-release-note:bceff0b3978c -->
- Reject chat streams without a finish reason (#10257). <!-- maestro-release-note:6370cc015330 -->
- Remove redundant provider request borrows (#10256). <!-- maestro-release-note:e768f18de0c9 -->
- Stop model fallback after turn budget exhaustion (#10248). <!-- maestro-release-note:5173e20182b2 -->
- Cap hosted retry delay after jitter (#10247). <!-- maestro-release-note:d06d8e9ffc21 -->
- End final controller turn without retry wake (#10253). <!-- maestro-release-note:aa49bdbc2ef2 -->
- Attest credential references at provider boundary (#10252). <!-- maestro-release-note:96e7a202a697 -->
- Scope Mission test constructors (#10251). <!-- maestro-release-note:099e8b8f4871 -->
- Stop retries after turn step budget exhaustion (#10246). <!-- maestro-release-note:b6ed6505c94d -->

## [0.10.93] - 2026-09-23

### Added
Expand Down
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.

17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,23 @@ deixic-code serve --port 3000 # HTTP runtime gateway
deixic-code hosted-runner
```

To continue a hosted Dex thread from the terminal, sign in with
`deixic-code evalops login`, then attach using its `thread:` id:

```sh
deixic-code thread attach thread:example
deixic-code thread attach thread:example --message "Check the latest result" --json
```

The terminal reads the Platform-owned thread, submits messages through the
same tenant-scoped owner API as the Deixic UI, and shows safe execution events.
When a turn needs approval or input, use `/respond <request-id> <action> [text]`
after reviewing the request. The CLI never grants an approval automatically.
`remote attach` is a separate RunnerSession transport and does not attach to a
Dex thread. A disconnected client can reattach without restarting the resident
turn. The one-shot form stops following after two minutes and leaves accepted
work running on Platform.

For a multi-feature coding task, prepare a mission contract and run it through
the native workflow scheduler. See [Run a local coding mission](docs/MISSION_RUN.md).

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@evalops/deixic-code",
"description": "Deixic Code — native Rust coding agent, CLI, TUI, and runtime gateway",
"version": "0.10.93",
"version": "0.10.94",
"private": false,
"type": "module",
"bin": {
Expand Down
124 changes: 92 additions & 32 deletions packages/ai-rs/src/openai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2734,12 +2734,10 @@ impl OpenAiClient {
if let Some(usage) = resp.get("usage") {
let input = usage
.get("input_tokens")
.and_then(serde_json::Value::as_u64)
.unwrap_or(0);
.and_then(serde_json::Value::as_u64);
let output = usage
.get("output_tokens")
.and_then(serde_json::Value::as_u64)
.unwrap_or(0);
.and_then(serde_json::Value::as_u64);
// Extract cached tokens from input_tokens_details
let cache_read = usage
.get("input_tokens_details")
Expand Down Expand Up @@ -2768,16 +2766,18 @@ impl OpenAiClient {
details.get("cache_write_tokens")
})
.and_then(serde_json::Value::as_u64);
let _ = tx.send(StreamEvent::Usage {
input_tokens: uncached_input_tokens(
input,
cache_read,
cache_write,
),
output_tokens: output,
cache_read_tokens: cache_read,
cache_creation_tokens: cache_write,
});
if let (Some(input), Some(output)) = (input, output) {
let _ = tx.send(StreamEvent::Usage {
input_tokens: uncached_input_tokens(
input,
cache_read,
cache_write,
),
output_tokens: output,
cache_read_tokens: cache_read,
cache_creation_tokens: cache_write,
});
}
}
}
let _ = tx.send(StreamEvent::MessageStop { stop_reason: None });
Expand Down Expand Up @@ -3067,28 +3067,28 @@ impl OpenAiClient {
let _ =
tx.send(StreamEvent::ProviderCost { cost_usd });
}
let _ = tx.send(StreamEvent::Usage {
input_tokens: uncached_input_tokens(
usage.prompt_tokens.unwrap_or(0),
usage
.prompt_tokens_details
.as_ref()
.and_then(|d| d.cached_tokens),
usage
.prompt_tokens_details
.as_ref()
.and_then(|d| d.cache_write_tokens),
),
output_tokens: usage.completion_tokens.unwrap_or(0),
cache_read_tokens: usage
if let (Some(input), Some(output)) =
(usage.prompt_tokens, usage.completion_tokens)
{
let cache_read = usage
.prompt_tokens_details
.as_ref()
.and_then(|d| d.cached_tokens),
cache_creation_tokens: usage
.and_then(|d| d.cached_tokens);
let cache_write = usage
.prompt_tokens_details
.as_ref()
.and_then(|d| d.cache_write_tokens),
});
.and_then(|d| d.cache_write_tokens);
let _ = tx.send(StreamEvent::Usage {
input_tokens: uncached_input_tokens(
input,
cache_read,
cache_write,
),
output_tokens: output,
cache_read_tokens: cache_read,
cache_creation_tokens: cache_write,
});
}
}
}
}
Expand Down Expand Up @@ -4149,6 +4149,66 @@ data: {"type":"response.completed","response":{"output":[{"type":"message","cont
}
}

#[tokio::test]
async fn managed_chat_partial_usage_does_not_become_zero_usage() {
for usage in ["{}", "{\"prompt_tokens\":9}", "{\"completion_tokens\":4}"] {
let sse = format!(
"data: {{\"id\":\"chat-1\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"gpt-5.6\",\"choices\":[{{\"index\":0,\"delta\":{{\"content\":\"ok\"}},\"finish_reason\":\"stop\"}}]}}\n\ndata: {{\"id\":\"chat-1\",\"object\":\"chat.completion.chunk\",\"created\":1,\"model\":\"gpt-5.6\",\"choices\":[],\"usage\":{usage}}}\n\ndata: [DONE]\n\n"
);
let (mut client, _request_rx) =
managed_gateway_test_client(&sse, &managed_receipt_headers());
let mut authorization: serde_json::Value =
serde_json::from_str(&managed_authorization_fixture("lineage-receipt")).unwrap();
authorization["claims"]["endpoint"] = "chat.completions".into();
client.set_managed_inference_authorization(Some(authorization.to_string()));
let stream = client
.stream(
&[],
&RequestConfig {
model: "gpt-5.6".into(),
..Default::default()
},
)
.await
.unwrap();
let events = collect_stream_events(stream).await;
assert!(
!events
.iter()
.any(|event| matches!(event, StreamEvent::Usage { .. })),
"partial usage must remain unknown: {events:?}"
);
}
}

#[tokio::test]
async fn managed_responses_partial_usage_does_not_become_zero_usage() {
let sse = "data: {\"type\":\"response.completed\",\"response\":{\"output\":[{\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"text\":\"ok\"}]}],\"usage\":{\"input_tokens\":9}}}\n\n";
let (mut client, _request_rx) =
managed_gateway_test_client(sse, &managed_receipt_headers());
let mut authorization: serde_json::Value =
serde_json::from_str(&managed_authorization_fixture("lineage-receipt")).unwrap();
authorization["claims"]["endpoint"] = "responses".into();
client.set_managed_inference_authorization(Some(authorization.to_string()));
let stream = client
.stream(
&[],
&RequestConfig {
model: "gpt-5.6".into(),
..Default::default()
},
)
.await
.unwrap();
let events = collect_stream_events(stream).await;
assert!(
!events
.iter()
.any(|event| matches!(event, StreamEvent::Usage { .. })),
"partial usage must remain unknown: {events:?}"
);
}

#[tokio::test]
async fn managed_endpoint_selects_url_body_and_stream_parser_over_catalog() {
for (endpoint, model, sse, path, field) in [
Expand Down
2 changes: 1 addition & 1 deletion packages/local-host-rs/src/credential_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ fn current_verified_identity_session_with_env()

/// Load and verify the current human Identity session for a product-owned
/// child operation such as Session History ingestion.
pub(crate) fn verified_current_identity_session() -> Result<PlatformSession> {
pub fn verified_current_identity_session() -> Result<PlatformSession> {
crate::safety::require_vendor_network()?;
let env = std::env::vars().collect::<HashMap<String, String>>();
let snapshot = if platform_session_from(None, &env).is_some() {
Expand Down
2 changes: 1 addition & 1 deletion packages/maestro-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "maestro"
version = "0.10.93"
version = "0.10.94"
edition = "2021"
license = "MIT"
description = "Canonical native Rust CLI for Deixic Code"
Expand Down
2 changes: 1 addition & 1 deletion packages/maestro-rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn pre_main_hardening() {
maestro_tui::process_hardening::pre_main_hardening();
}

const HELP: &str = "Deixic Code\n\nUsage:\n deixic-code [options] [prompt]\n deixic-code exec <prompt>\n deixic-code -w <name> [prompt]\n deixic-code doctor [--json] [--live] [--verbose] [--timeout <ms>] [probe groups]\n deixic-code setup [--json] [--live] [--model <provider/model>]\n deixic-code --list-tools [--json]\n deixic-code --headless\n deixic-code serve [--port <port>] [--parent-pid <pid> | --liveness-fd <fd>]\n deixic-code hosted-runner [options]\n\nCommon commands:\n deixic-code setup Check authentication and show the next setup step\n deixic-code config Inspect or change Deixic Code configuration\n deixic-code experiments Review or change tool experiment participation\n deixic-code models List available models and providers\n deixic-code sessions List, export, or import saved sessions\n deixic-code scenario Run or inspect scripted scenarios\n deixic-code run Inspect or replay a saved session\n\nAdvanced commands:\n deixic-code diagnostics Project compiler JSONL, optionally against a prior observation\n deixic-code codex Manage Codex authentication and app-server access\n deixic-code mcp Configure MCP servers\n deixic-code plugins Inspect and manage plugins\n deixic-code connections Manage API keys and delegated accounts\n deixic-code init Register a Deixic agent integration\n\nCompatibility:\n maestro remains available as an alias; MAESTRO_* variables and .maestro paths are unchanged.\n\nOptions:\n -w, --worktree <name> Run the session in a new git worktree at ../<repo>-wt-<name>\n on a new branch; interactive worktrees and branches are kept\n exec removes only clean worktrees with no new commits\n\nThe product runtime is native Rust; no Node.js or Bun runtime is required.";
const HELP: &str = "Deixic Code\n\nUsage:\n deixic-code [options] [prompt]\n deixic-code exec <prompt>\n deixic-code -w <name> [prompt]\n deixic-code doctor [--json] [--live] [--verbose] [--timeout <ms>] [probe groups]\n deixic-code setup [--json] [--live] [--model <provider/model>]\n deixic-code --list-tools [--json]\n deixic-code --headless\n deixic-code serve [--port <port>] [--parent-pid <pid> | --liveness-fd <fd>]\n deixic-code hosted-runner [options]\n\nCommon commands:\n deixic-code setup Check authentication and show the next setup step\n deixic-code config Inspect or change Deixic Code configuration\n deixic-code experiments Review or change tool experiment participation\n deixic-code models List available models and providers\n deixic-code sessions List, export, or import saved sessions\n deixic-code thread Attach to a hosted Dex thread\n deixic-code scenario Run or inspect scripted scenarios\n deixic-code run Inspect or replay a saved session\n\nAdvanced commands:\n deixic-code diagnostics Project compiler JSONL, optionally against a prior observation\n deixic-code codex Manage Codex authentication and app-server access\n deixic-code mcp Configure MCP servers\n deixic-code plugins Inspect and manage plugins\n deixic-code connections Manage API keys and delegated accounts\n deixic-code init Register a Deixic agent integration\n\nCompatibility:\n maestro remains available as an alias; MAESTRO_* variables and .maestro paths are unchanged.\n\nOptions:\n -w, --worktree <name> Run the session in a new git worktree at ../<repo>-wt-<name>\n on a new branch; interactive worktrees and branches are kept\n exec removes only clean worktrees with no new commits\n\nThe product runtime is native Rust; no Node.js or Bun runtime is required.";
const VERSION: &str = concat!("deixic-code ", env!("CARGO_PKG_VERSION"));

fn sync_command_output(command: &Command) -> Option<&'static str> {
Expand Down
Loading
Loading