Nucleic: Session Routing Logic Enhancement

This commit is contained in:
2026-07-11 22:48:04 -07:00
parent 85ce7725ce
commit 09d4616cd1
2 changed files with 18 additions and 11 deletions
@@ -121,10 +121,13 @@ final class RemoteStore: ObservableObject {
return (descriptor, candidates) return (descriptor, candidates)
} }
/// Dispatch a new chat to the abstract **Mesh** destination from the phone: rank eligible /// Dispatch a new chat to the abstract **Carbon** destination from the phone: rank eligible
/// hosts by the shared scorer and try them in order, one at a time with a correlated, /// hosts by the shared scorer and try them in order, one at a time with a correlated,
/// idempotent ack (a timeout retries the same candidate once before moving on). On success /// idempotent ack (a timeout retries the same candidate once before moving on). On success
/// routes to the landed session. Mirrors `AppStore.dispatchChatToMesh`. /// routes to the landed session. Mirrors `AppStore.dispatchChatToMesh`. The phone stamps
/// itself as the Carbon origin, so the landed session is Carbon-managed (its owner keeps it
/// on the optimal host between turns); the phone can't own sessions, so the origin exclusion
/// is naturally satisfied by every candidate.
func dispatchChatToMesh( func dispatchChatToMesh(
projectID: ProjectID, message: String, model: String? = nil, projectID: ProjectID, message: String, model: String? = nil,
effort: String? = nil, auto: Bool? = nil effort: String? = nil, auto: Bool? = nil
@@ -141,8 +144,8 @@ final class RemoteStore: ObservableObject {
backend: nil, excluding: excluded).first backend: nil, excluding: excluded).first
else { else {
showError(tried == 0 showError(tried == 0
? "No mesh runner can take this chat right now." ? "No Carbon host can take this chat right now."
: "No mesh runner could take this chat (\(tried) tried).", sessionID: nil) : "No Carbon host could take this chat (\(tried) tried).", sessionID: nil)
return return
} }
tried += 1 tried += 1
@@ -154,7 +157,8 @@ final class RemoteStore: ObservableObject {
let requestID = UUID().uuidString let requestID = UUID().uuidString
let request = StartChatRequest( let request = StartChatRequest(
projectID: targetProjectID, message: text, model: model, effort: effort, projectID: targetProjectID, message: text, model: model, effort: effort,
auto: auto, requestID: requestID) auto: auto, requestID: requestID,
carbonOriginDeviceID: IdentityStore.deviceID())
var outcome = await sendDispatch(request, on: conn, timeout: .seconds(10)) var outcome = await sendDispatch(request, on: conn, timeout: .seconds(10))
if outcome == nil { if outcome == nil {
outcome = await sendDispatch(request, on: conn, timeout: .seconds(5)) outcome = await sendDispatch(request, on: conn, timeout: .seconds(5))
@@ -18,8 +18,10 @@ struct StartChatComposer: View {
/// project page's "+", so the chat always lands in that project. /// project page's "+", so the chat always lands in that project.
var lockedProject: WireProject? = nil var lockedProject: WireProject? = nil
@State private var projectID: ProjectID? @State private var projectID: ProjectID?
/// Mesh dispatch: run on the project's own Mac (false) or auto-route to the best eligible /// Carbon (mesh work queue): run on the project's own Mac (false) or on Carbon (true) —
/// mesh host (true). Shown only when a connected peer could actually take it. /// auto-routed to the best eligible mesh host and kept on the optimal host between turns.
/// The phone itself can't run sessions, so every Carbon candidate is already "not here".
/// Shown only when a connected peer could actually take it.
@State private var runOnMesh = false @State private var runOnMesh = false
@State private var autoOverride: Bool? @State private var autoOverride: Bool?
@State private var model: String? @State private var model: String?
@@ -87,18 +89,19 @@ struct StartChatComposer: View {
? "Auto-approve is always on for Nucleic Control chats — they run autonomously." ? "Auto-approve is always on for Nucleic Control chats — they run autonomously."
: "Auto-approve safe actions; destructive ones still ask.") : "Auto-approve safe actions; destructive ones still ask.")
} }
// Mesh dispatch: when an eligible peer holds this repo, offer "Auto (Mesh)" so the // Carbon (mesh work queue): when an eligible peer holds this repo, offer "Carbon" so
// chat routes to whichever host fits rather than always its own owner. // the chat routes to whichever host fits — and moves between hosts as availability
// changes — rather than always running on its own owner.
if meshAvailable { if meshAvailable {
Menu { Menu {
Picker("Run on", selection: $runOnMesh) { Picker("Run on", selection: $runOnMesh) {
Text("This Mac").tag(false) Text("This Mac").tag(false)
Text("Mesh (automatic)").tag(true) Text("Carbon").tag(true)
} }
.pickerStyle(.inline) .pickerStyle(.inline)
} label: { } label: {
Label( Label(
runOnMesh ? "Mesh" : "This Mac", runOnMesh ? "Carbon" : "This Mac",
systemImage: runOnMesh ? "antenna.radiowaves.left.and.right" : "desktopcomputer") systemImage: runOnMesh ? "antenna.radiowaves.left.and.right" : "desktopcomputer")
.font(.caption) .font(.caption)
.foregroundStyle(Palette.accent) .foregroundStyle(Palette.accent)