Merge nucleic/warm-quartz-marten-mswp into dev
This commit is contained in:
@@ -6,18 +6,16 @@ import NucleicProtocol
|
|||||||
/// The division of labor with the Mac is deliberate and worth stating once here, because it's
|
/// The division of labor with the Mac is deliberate and worth stating once here, because it's
|
||||||
/// what makes a rail on a thin remote client honest:
|
/// what makes a rail on a thin remote client honest:
|
||||||
///
|
///
|
||||||
/// - **The host routes.** The matrix, the connected providers, the Settings pin, live quota and
|
/// - **The host publishes the routes.** The matrix, connected providers, Settings pin, live quota,
|
||||||
/// provider incidents live on the Mac. A send carries the rail's *stop*, never a concrete pair
|
/// and provider incidents live on the Mac. The phone only chooses among the concrete routes in
|
||||||
/// (`StartChatRequest.intelligence`, `ClientMsg.setSessionIntelligence`), and the host resolves
|
/// that projection; it does not recreate the matrix.
|
||||||
/// it with its own classifier. So the phone can never pin a worse model than the Mac would.
|
|
||||||
/// - **The phone previews.** It runs the shared `HeuristicPurposeClassifier` — the same code the
|
/// - **The phone previews.** It runs the shared `HeuristicPurposeClassifier` — the same code the
|
||||||
/// host runs, moved into NucleicProtocol precisely so both sides classify identically — over
|
/// host runs, moved into NucleicProtocol precisely so both sides classify identically — over
|
||||||
/// the draft, and looks the answer up in the host's projected route table. Sub-millisecond, no
|
/// the draft, and looks the answer up in the host's projected route table. Sub-millisecond, no
|
||||||
/// round trip, so the route line can follow every keystroke on a cellular link.
|
/// round trip, so the route line can follow every keystroke on a cellular link.
|
||||||
/// - **The preview can be wrong, and says so by being a preview.** The host's send-time
|
/// - **The displayed pair is the launch authority.** A new-chat send carries the stop *and* the
|
||||||
/// classification runs a fuller stack; when the two disagree, the transcript's routing note
|
/// projected model/effort pair. The host preserves that explicit pair rather than classifying a
|
||||||
/// records what actually ran. That's the same relationship the Mac's own pre-send preview has
|
/// second time, so the session cannot start on a model the route line never showed.
|
||||||
/// with its send-time resolve.
|
|
||||||
extension RemoteStore {
|
extension RemoteStore {
|
||||||
|
|
||||||
/// Whether the context host offers the rail at all. False keeps the composers on their manual
|
/// Whether the context host offers the rail at all. False keeps the composers on their manual
|
||||||
|
|||||||
@@ -2741,10 +2741,10 @@ final class RemoteStore: ObservableObject {
|
|||||||
|
|
||||||
// MARK: - Control intents (control scope; the same actions the Mac can take)
|
// MARK: - Control intents (control scope; the same actions the Mac can take)
|
||||||
|
|
||||||
/// Start a chat on the owning Mac. `intelligence` is the rail's stop: pass it and leave
|
/// Start a chat on the owning Mac. The routed composer passes `intelligence` together with
|
||||||
/// `model` nil to have the host classify the opening message and route it (the composer's
|
/// the concrete model/effort displayed from the host's projected route table: the stop keeps
|
||||||
/// normal path); pass a concrete `model`/`effort` instead when the user pinned one. Sending
|
/// the rail positioned, while the explicit pair guarantees the session runs what was shown.
|
||||||
/// both leaves the explicit pair in charge — see `AppStore.remoteStartSelection`.
|
/// Older clients may still pass only the stop and let the host resolve it.
|
||||||
func startChat(in projectID: ProjectID, message: String, model: String? = nil,
|
func startChat(in projectID: ProjectID, message: String, model: String? = nil,
|
||||||
effort: String? = nil, baseBranch: String? = nil,
|
effort: String? = nil, baseBranch: String? = nil,
|
||||||
useWorktree: Bool = true, auto: Bool? = nil,
|
useWorktree: Bool = true, auto: Bool? = nil,
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ struct StartChatComposer: View {
|
|||||||
orchestraAvailable: store.intelligenceCatalog.orchestraAvailable,
|
orchestraAvailable: store.intelligenceCatalog.orchestraAvailable,
|
||||||
composerText: draft,
|
composerText: draft,
|
||||||
routeDescription: store.intelligenceRouteDescription(
|
routeDescription: store.intelligenceRouteDescription(
|
||||||
for: draft, level: previewLevel),
|
for: routingPrompt, level: previewLevel),
|
||||||
unavailableReason: store.canControl ? nil : "This device is view-only.",
|
unavailableReason: store.canControl ? nil : "This device is view-only.",
|
||||||
onPreviewChanged: { intelligencePreview = $0 })
|
onPreviewChanged: { intelligencePreview = $0 })
|
||||||
IntelligenceRouteLine(
|
IntelligenceRouteLine(
|
||||||
@@ -317,27 +317,47 @@ struct StartChatComposer: View {
|
|||||||
private var previewLevel: Int { intelligencePreview ?? intelligence }
|
private var previewLevel: Int { intelligencePreview ?? intelligence }
|
||||||
|
|
||||||
private var previewRoute: WireIntelligenceCatalog.Route? {
|
private var previewRoute: WireIntelligenceCatalog.Route? {
|
||||||
store.intelligenceRoute(for: draft, level: previewLevel)
|
store.intelligenceRoute(for: routingPrompt, level: previewLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The route at the committed stop, distinct from the under-finger preview. This exact pair is
|
||||||
|
/// copied into the start request so the host cannot classify again and run something other
|
||||||
|
/// than the model and effort named by the route line.
|
||||||
|
private var committedRoute: WireIntelligenceCatalog.Route? {
|
||||||
|
store.intelligenceRoute(for: routingPrompt, level: intelligence)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attachment-only starts still need a visible concrete route. The host ultimately adds file
|
||||||
|
/// references to the opening prompt; filenames give the shared classifier equivalent context
|
||||||
|
/// before launch and, critically, are used by both the route line and the committed request.
|
||||||
|
private var routingPrompt: String {
|
||||||
|
let text = draft.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard text.isEmpty, !attachments.isEmpty else { return draft }
|
||||||
|
return "Work with the attached files: " + attachments.map(\.filename).joined(separator: ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var orchestraActive: Bool { orchestra || MobileEfforts.isOrchestra(effort) }
|
private var orchestraActive: Bool { orchestra || MobileEfforts.isOrchestra(effort) }
|
||||||
|
|
||||||
private var canStart: Bool {
|
private var canStart: Bool {
|
||||||
selected != nil && store.canControl
|
guard selected != nil, store.canControl,
|
||||||
&& !(draft.trimmingCharacters(in: .whitespaces).isEmpty && attachments.isEmpty)
|
!(draft.trimmingCharacters(in: .whitespaces).isEmpty && attachments.isEmpty)
|
||||||
|
else { return false }
|
||||||
|
guard store.routesIntelligence, !orchestra else { return true }
|
||||||
|
return committedRoute?.isAvailable == true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the chat. When the rail is driving, the request carries the **stop** and no model:
|
/// Start the chat. When the rail is driving, the request carries both its stop and the exact
|
||||||
/// the host classifies the real prompt with its own stack and picks the pair, so the phone
|
/// concrete pair shown on screen. The stop preserves the rail position; the pair is the launch
|
||||||
/// never pins a worse model than the Mac would have (see `RemoteIntelligence`). A host that
|
/// authority, preventing a second host-side classification from contradicting the route line.
|
||||||
/// doesn't route sends the concrete values from its manual menus instead.
|
|
||||||
private func start() {
|
private func start() {
|
||||||
guard let project = selected else { return }
|
guard let project = selected else { return }
|
||||||
let routed = store.routesIntelligence && !orchestra
|
let routed = store.routesIntelligence && !orchestra
|
||||||
let sentModel = routed ? nil : model
|
let route = routed ? committedRoute : nil
|
||||||
|
guard !routed || route?.isAvailable == true else { return }
|
||||||
|
let sentModel = routed ? route?.model : model
|
||||||
let sentEffort = orchestra
|
let sentEffort = orchestra
|
||||||
? store.modelCatalog.orchestraSentinelOrFallback
|
? store.modelCatalog.orchestraSentinelOrFallback
|
||||||
: (routed ? nil : effort)
|
: (routed ? route?.effort : effort)
|
||||||
let sentLevel = store.routesIntelligence ? intelligence : nil
|
let sentLevel = store.routesIntelligence ? intelligence : nil
|
||||||
|
|
||||||
if runOnMesh && meshAvailable {
|
if runOnMesh && meshAvailable {
|
||||||
|
|||||||
Reference in New Issue
Block a user