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
8 changes: 4 additions & 4 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": "36bd59e4bac6e289aaf493f38e26f42fc382c4b3",
"sourceSha": "393b500389b42a02222404dfb7bbe3a3586871f7",
"destinationRepository": "dx-corp/code",
"priorProjectedBase": "dccebe04146633d0841585f1d511aceda5ea746d",
"priorProjectedBase": "a775955b5d82cdc7da4d5c41d1c2083b8c208e4d",
"definitionDigest": "82936441c776e3e8edb5d215a75007ec9714a233f489d460075d79d5ef5ba32f",
"toolDigest": "45502ff0478e541d02f34cc39ac332935f8d3c0030fa221afe5bcc3b5e51f88e",
"contentDigest": "78b16e2e7b3b23f64ba5345cbae3d36206cdf4d1df28f1756e5ecf53cfb2d4ea",
"toolDigest": "7f2d7ef1750a1809dd90f3bd7e50c4220784a8a9e0b3dfd499355e509132c32f",
"contentDigest": "29e7e643aca0e259d6bf6c423110fa84fe86a8dc49f33b85e0d4770228debcf1",
"publicationEligible": true
}
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,20 @@ deixic-code thread attach thread:example --message "Check the latest result" --j
```

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.
tenant-scoped public application API, 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.

Native thread, readiness, managed setup, and issue-report clients use generated
`deixicpublic.v1.DeixicPublicService` bindings from
[the public contract](proto/deixicpublic/v1/sdk.proto). The release build and staged
release verifier audit the native bytes for private protocol namespaces before
publication. The contract copy is checked against the canonical public source in Mono.

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
6 changes: 3 additions & 3 deletions packages/local-host-rs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?);
let proto_dir = manifest_dir.join("../../proto");
let readiness_proto = proto_dir.join("console/v1/managed_inference.proto");
println!("cargo:rerun-if-changed={}", readiness_proto.display());
let public_proto = proto_dir.join("deixicpublic/v1/sdk.proto");
println!("cargo:rerun-if-changed={}", public_proto.display());
let proto_file = proto_dir.join("maestro/v1/headless.proto");

println!("cargo:rerun-if-changed=build.rs");
Expand All @@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
config.extern_path(".google.protobuf.Struct", "::prost_types::Struct");
config.extern_path(".google.protobuf.ListValue", "::prost_types::ListValue");
config.extern_path(".google.protobuf.NullValue", "::prost_types::NullValue");
config.compile_protos(&[proto_file, readiness_proto], &[proto_dir])?;
config.compile_protos(&[proto_file, public_proto], &[proto_dir])?;

Ok(())
}
88 changes: 34 additions & 54 deletions packages/local-host-rs/src/bug_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::session::CustomEntry;
use crate::session::{SessionEntry, SessionManager};

const ENTRY_TYPE: &str = "product_issue_draft_v1";
const SUBMIT_PATH: &str = "/deixic.v1.DeixicService/SubmitNativeProductIssueReport";
const SUBMIT_PATH: &str = "/deixicpublic.v1.DeixicPublicService/SubmitIssueReport";

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Destination {
Expand Down Expand Up @@ -263,7 +263,7 @@ impl FeedbackClient {
"The report must be reviewed and saved before sending."
);
let request = SubmitRequest {
query: Some(ReportQuery {
scope: Some(ReportQuery {
organization_id: self.destination.organization_id.clone(),
workspace_id: self.destination.workspace_id.clone(),
}),
Expand All @@ -276,7 +276,7 @@ impl FeedbackClient {
},
include_diagnostics: report.include_diagnostics,
idempotency_key: report.id.clone(),
context: report.outgoing_context(),
context: report.outgoing_context().map(Into::into),
};
let response = self.http.post(&self.destination.endpoint).bearer_auth(&self.token)
.header("connect-protocol-version", "1")
Expand Down Expand Up @@ -345,53 +345,39 @@ fn endpoint(base: &str) -> Result<String> {
Ok(url.into())
}

// Bounded native projection of proto/console/v1/console.proto. Wire tags are
// verified against a shared fixture decoded by the production service tests.
#[derive(Clone, PartialEq, Message)]
struct SubmitRequest {
#[prost(message, optional, tag = "1")]
query: Option<ReportQuery>,
#[prost(string, tag = "2")]
description: String,
#[prost(string, tag = "3")]
expected_behavior: String,
#[prost(string, tag = "5")]
app_version: String,
#[prost(bool, tag = "11")]
include_diagnostics: bool,
#[prost(string, tag = "12")]
idempotency_key: String,
#[prost(message, optional, tag = "13")]
context: Option<ReportContext>,
}
#[derive(Clone, PartialEq, Message)]
struct ReportQuery {
#[prost(string, tag = "13")]
organization_id: String,
#[prost(string, tag = "1")]
workspace_id: String,
}
#[derive(Clone, PartialEq, Message)]
struct SubmitResponse {
#[prost(message, optional, tag = "1")]
report: Option<ReportReceipt>,
}
#[derive(Clone, PartialEq, Message)]
struct ReportReceipt {
#[prost(string, tag = "1")]
id: String,
#[prost(string, tag = "2")]
reference: String,
#[cfg(test)]
use crate::public_protocol::IssueReport as ReportReceipt;
use crate::public_protocol::{
IssueContext, IssueEvidence, Scope as ReportQuery, SubmitIssueReportRequest as SubmitRequest,
SubmitIssueReportResponse as SubmitResponse,
};

impl From<ReportContext> for IssueContext {
fn from(context: ReportContext) -> Self {
Self {
reproduction_steps: context.reproduction_steps,
model: context.model,
evidence: context
.evidence
.into_iter()
.map(|e| IssueEvidence {
kind: e.kind,
source_id: e.source_id,
text: e.text,
})
.collect(),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn native_wire_matches_the_fixture_read_by_the_product_issue_service() {
fn public_issue_report_wire_matches_reviewed_fixtures() {
let mut request = SubmitRequest {
context: None,
query: Some(ReportQuery {
scope: Some(ReportQuery {
organization_id: "org-1".into(),
workspace_id: "workspace-1".into(),
}),
Expand All @@ -408,12 +394,12 @@ mod tests {
}
assert_eq!(
encoded,
include_str!("../../../test/fixtures/product-issue-report-native-v1.hex").trim()
include_str!("../../../test/fixtures/product-issue-report-public-v1.hex").trim()
);
request.context = Some(ReportContext {
request.context = Some(IssueContext {
reproduction_steps: "Repeat failing tool".into(),
model: "test-model".into(),
evidence: vec![ReportEvidence {
evidence: vec![IssueEvidence {
kind: "tool_result".into(),
source_id: "call-1".into(),
text: "wrong action".into(),
Expand All @@ -425,7 +411,7 @@ mod tests {
}
assert_eq!(
encoded,
include_str!("../../../test/fixtures/product-issue-report-native-v2.hex").trim()
include_str!("../../../test/fixtures/product-issue-report-public-v2.hex").trim()
);
}

Expand Down Expand Up @@ -546,26 +532,20 @@ fn now_seconds() -> i64 {
chrono::Utc::now().timestamp()
}

#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ReportContext {
#[prost(string, tag = "1")]
#[serde(default)]
pub reproduction_steps: String,
#[prost(string, tag = "2")]
#[serde(default)]
pub model: String,
#[prost(message, repeated, tag = "3")]
#[serde(default)]
pub evidence: Vec<ReportEvidence>,
}

#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ReportEvidence {
#[prost(string, tag = "1")]
pub kind: String,
#[prost(string, tag = "2")]
pub source_id: String,
#[prost(string, tag = "3")]
pub text: String,
}

Expand Down
17 changes: 10 additions & 7 deletions packages/local-host-rs/src/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1694,16 +1694,17 @@ mod tests {
.build()
.expect("runtime")
.block_on(async {
// Independently encode the canonical response tags (version=1,
// organization_id=7, workspace_id=8, MCP policy=5).
// Independently encode the public response: scope=1, version=2, MCP=6.
for (organization, expected) in [
("org-test", CheckStatus::Pass),
("other-org", CheckStatus::Warning),
] {
let mut body = vec![0x08, 1, 0x3a, organization.len() as u8];
body.extend_from_slice(organization.as_bytes());
body.extend_from_slice(b"\x42\x0eworkspace-test");
body.extend_from_slice(&[0x2a, 2, 0x08, 2]);
let mut scope = vec![0x0a, organization.len() as u8];
scope.extend_from_slice(organization.as_bytes());
scope.extend_from_slice(b"\x12\x0eworkspace-test");
let mut body = vec![0x0a, scope.len() as u8];
body.extend_from_slice(&scope);
body.extend_from_slice(&[0x10, 1, 0x32, 2, 0x08, 2]);
let (base, server) =
test_server_bytes(200, body, "application/proto", Duration::ZERO).await;
std::env::set_var("MAESTRO_MANAGED_SETUP_URL", base);
Expand All @@ -1717,7 +1718,9 @@ mod tests {
assert_eq!(report.status, expected, "{report:?}");
assert!(report.live);
let request = server.await.expect("server");
assert!(request.contains("/console.v1.ManagedSetupService/GetManagedSetup"));
assert!(
request.contains("/deixicpublic.v1.DeixicPublicService/GetClientSetup")
);
assert!(
!serde_json::to_string(&report)
.expect("report")
Expand Down
24 changes: 24 additions & 0 deletions packages/local-host-rs/src/headless/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,10 @@ pub struct GovernedToolGrant {
/// Platform-owned process definition instructions installed in the system role.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub process_system_prompt: Option<String>,
/// Exact Platform-admitted Agent Registry publication and immutable
/// PromptService snapshots for this turn. It grants no tools by itself.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_profile: Option<AgentProfileTurnContext>,
pub envelope_version: u32,
pub grant_id: String,
pub grant_version: u64,
Expand Down Expand Up @@ -657,6 +661,26 @@ pub struct GovernedToolGrant {
pub connection_bindings: Vec<ConnectionGrantBinding>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default, deny_unknown_fields, rename_all = "camelCase")]
pub struct AgentProfileTurnContext {
pub agent_id: String,
pub config_version: i32,
pub digest_sha256: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub instructions: Vec<AgentProfileInstruction>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default, deny_unknown_fields, rename_all = "camelCase")]
pub struct AgentProfileInstruction {
pub name: String,
pub prompt_id: String,
pub version_id: String,
pub content_digest_sha256: String,
pub content: String,
}

impl GovernedToolGrant {
/// Stable identity used for reconnect replay and duplicate-turn matching.
#[must_use]
Expand Down
1 change: 1 addition & 0 deletions packages/local-host-rs/src/headless/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod tests {
RuntimeGovernedToolGrant {
process_budget: None,
process_system_prompt: None,
agent_profile: None,
envelope_version: 2,
grant_id: "grant-1".into(),
grant_version: 1,
Expand Down
Loading
Loading