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)
}
/// 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,
/// 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(
projectID: ProjectID, message: String, model: String? = nil,
effort: String? = nil, auto: Bool? = nil
@@ -141,8 +144,8 @@ final class RemoteStore: ObservableObject {
backend: nil, excluding: excluded).first
else {
showError(tried == 0
? "No mesh runner can take this chat right now."
: "No mesh runner could take this chat (\(tried) tried).", sessionID: nil)
? "No Carbon host can take this chat right now."
: "No Carbon host could take this chat (\(tried) tried).", sessionID: nil)
return
}
tried += 1
@@ -154,7 +157,8 @@ final class RemoteStore: ObservableObject {
let requestID = UUID().uuidString
let request = StartChatRequest(
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))
if outcome == nil {
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.
var lockedProject: WireProject? = nil
@State private var projectID: ProjectID?
/// Mesh dispatch: run on the project's own Mac (false) or auto-route to the best eligible
/// mesh host (true). Shown only when a connected peer could actually take it.
/// Carbon (mesh work queue): run on the project's own Mac (false) or on Carbon (true) —
/// 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 autoOverride: Bool?
@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 safe actions; destructive ones still ask.")
}
// Mesh dispatch: when an eligible peer holds this repo, offer "Auto (Mesh)" so the
// chat routes to whichever host fits rather than always its own owner.
// Carbon (mesh work queue): when an eligible peer holds this repo, offer "Carbon" so
// the chat routes to whichever host fits — and moves between hosts as availability
// changes — rather than always running on its own owner.
if meshAvailable {
Menu {
Picker("Run on", selection: $runOnMesh) {
Text("This Mac").tag(false)
Text("Mesh (automatic)").tag(true)
Text("Carbon").tag(true)
}
.pickerStyle(.inline)
} label: {
Label(
runOnMesh ? "Mesh" : "This Mac",
runOnMesh ? "Carbon" : "This Mac",
systemImage: runOnMesh ? "antenna.radiowaves.left.and.right" : "desktopcomputer")
.font(.caption)
.foregroundStyle(Palette.accent)