@lattices/cli 0.4.13 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +5 -7
  2. package/apps/mac/Info.plist +2 -2
  3. package/apps/mac/Lattices.app/Contents/Info.plist +4 -12
  4. package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/bin/lattices-app.ts +110 -17
  6. package/bin/lattices-build +125 -0
  7. package/bin/lattices-dev +89 -16
  8. package/bin/lattices.ts +977 -16
  9. package/docs/agents.md +81 -4
  10. package/docs/ai-chat-ux-review.md +416 -0
  11. package/docs/api.md +135 -3
  12. package/docs/app.md +30 -8
  13. package/docs/config.md +4 -0
  14. package/docs/mouse-gestures.md +191 -63
  15. package/docs/proposals/LAT-004-interactive-overlay-actors.md +1 -1
  16. package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
  17. package/docs/proposals/LAT-006-mira-in-lattices.md +553 -0
  18. package/docs/reference/dewey.config.ts +2 -2
  19. package/docs/release.md +171 -0
  20. package/docs/repo-structure.md +4 -5
  21. package/docs/voice.md +11 -27
  22. package/package.json +9 -10
  23. package/apps/mac/Package.swift +0 -27
  24. package/apps/mac/Sources/AppShell/App.swift +0 -26
  25. package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +0 -27
  26. package/apps/mac/Sources/AppShell/AppDelegate.swift +0 -189
  27. package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +0 -25
  28. package/apps/mac/Sources/AppShell/AppShellView.swift +0 -171
  29. package/apps/mac/Sources/AppShell/AppUpdater.swift +0 -305
  30. package/apps/mac/Sources/AppShell/CliActionLauncher.swift +0 -50
  31. package/apps/mac/Sources/AppShell/HomeDashboardView.swift +0 -133
  32. package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +0 -87
  33. package/apps/mac/Sources/AppShell/KeyRecorderView.swift +0 -210
  34. package/apps/mac/Sources/AppShell/LatticesRuntime.swift +0 -104
  35. package/apps/mac/Sources/AppShell/MainView.swift +0 -847
  36. package/apps/mac/Sources/AppShell/MainWindow.swift +0 -83
  37. package/apps/mac/Sources/AppShell/MenuBarController.swift +0 -177
  38. package/apps/mac/Sources/AppShell/OnboardingView.swift +0 -483
  39. package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +0 -366
  40. package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +0 -70
  41. package/apps/mac/Sources/AppShell/Preferences.swift +0 -297
  42. package/apps/mac/Sources/AppShell/SettingsView.swift +0 -3163
  43. package/apps/mac/Sources/AppShell/SettingsWindow.swift +0 -34
  44. package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +0 -13
  45. package/apps/mac/Sources/Core/Actions/HotkeyManager.swift +0 -256
  46. package/apps/mac/Sources/Core/Actions/HotkeyStore.swift +0 -399
  47. package/apps/mac/Sources/Core/Actions/IntentEngine.swift +0 -988
  48. package/apps/mac/Sources/Core/Actions/IntentSchema.swift +0 -94
  49. package/apps/mac/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -54
  50. package/apps/mac/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -56
  51. package/apps/mac/Sources/Core/Actions/Intents/FocusIntent.swift +0 -69
  52. package/apps/mac/Sources/Core/Actions/Intents/HelpIntent.swift +0 -41
  53. package/apps/mac/Sources/Core/Actions/Intents/KillIntent.swift +0 -47
  54. package/apps/mac/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -53
  55. package/apps/mac/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -67
  56. package/apps/mac/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -32
  57. package/apps/mac/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -30
  58. package/apps/mac/Sources/Core/Actions/Intents/ScanIntent.swift +0 -52
  59. package/apps/mac/Sources/Core/Actions/Intents/SearchIntent.swift +0 -190
  60. package/apps/mac/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -50
  61. package/apps/mac/Sources/Core/Actions/Intents/TileIntent.swift +0 -61
  62. package/apps/mac/Sources/Core/Actions/PaletteCommand.swift +0 -439
  63. package/apps/mac/Sources/Core/Actions/VoiceIntentResolver.swift +0 -713
  64. package/apps/mac/Sources/Core/Companion/CompanionActivityLog.swift +0 -70
  65. package/apps/mac/Sources/Core/Companion/CompanionKeyboardController.swift +0 -141
  66. package/apps/mac/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -454
  67. package/apps/mac/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -555
  68. package/apps/mac/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -629
  69. package/apps/mac/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -204
  70. package/apps/mac/Sources/Core/Companion/LatticesDeckHost.swift +0 -1463
  71. package/apps/mac/Sources/Core/Daemon/DaemonProtocol.swift +0 -114
  72. package/apps/mac/Sources/Core/Daemon/DaemonServer.swift +0 -427
  73. package/apps/mac/Sources/Core/Daemon/LatticesApi.swift +0 -2965
  74. package/apps/mac/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -111
  75. package/apps/mac/Sources/Core/Desktop/AppTypeClassifier.swift +0 -106
  76. package/apps/mac/Sources/Core/Desktop/DesktopModel.swift +0 -331
  77. package/apps/mac/Sources/Core/Desktop/DesktopModelTypes.swift +0 -73
  78. package/apps/mac/Sources/Core/Desktop/InventoryManager.swift +0 -35
  79. package/apps/mac/Sources/Core/Desktop/InventoryPath.swift +0 -43
  80. package/apps/mac/Sources/Core/Desktop/MouseFinder.swift +0 -527
  81. package/apps/mac/Sources/Core/Desktop/OcrModel.swift +0 -467
  82. package/apps/mac/Sources/Core/Desktop/OcrStore.swift +0 -329
  83. package/apps/mac/Sources/Core/Desktop/PlacementSpec.swift +0 -195
  84. package/apps/mac/Sources/Core/Desktop/SessionWindowLocator.swift +0 -139
  85. package/apps/mac/Sources/Core/Desktop/TilePickerView.swift +0 -209
  86. package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +0 -33
  87. package/apps/mac/Sources/Core/Desktop/WindowDragSnapController.swift +0 -429
  88. package/apps/mac/Sources/Core/Desktop/WindowPreviewCard.swift +0 -100
  89. package/apps/mac/Sources/Core/Desktop/WindowPreviewStore.swift +0 -112
  90. package/apps/mac/Sources/Core/Desktop/WindowSelectionStore.swift +0 -76
  91. package/apps/mac/Sources/Core/Desktop/WindowTiler.swift +0 -2222
  92. package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +0 -124
  93. package/apps/mac/Sources/Core/Input/EventTapThread.swift +0 -54
  94. package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +0 -20
  95. package/apps/mac/Sources/Core/Input/KeyboardRemapConfig.swift +0 -69
  96. package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +0 -346
  97. package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +0 -141
  98. package/apps/mac/Sources/Core/Input/MouseGestureConfig.swift +0 -499
  99. package/apps/mac/Sources/Core/Input/MouseGestureController.swift +0 -2271
  100. package/apps/mac/Sources/Core/Input/MouseInputDeviceStore.swift +0 -98
  101. package/apps/mac/Sources/Core/Input/MouseInputEventViewer.swift +0 -272
  102. package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +0 -170
  103. package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +0 -39
  104. package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +0 -624
  105. package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +0 -56
  106. package/apps/mac/Sources/Core/Overlays/AppWindowShell.swift +0 -63
  107. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -1566
  108. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -1927
  109. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -196
  110. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -307
  111. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -67
  112. package/apps/mac/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -576
  113. package/apps/mac/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -279
  114. package/apps/mac/Sources/Core/Overlays/HUD/HUDController.swift +0 -1158
  115. package/apps/mac/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -849
  116. package/apps/mac/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -179
  117. package/apps/mac/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -596
  118. package/apps/mac/Sources/Core/Overlays/HUD/HUDState.swift +0 -367
  119. package/apps/mac/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -243
  120. package/apps/mac/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -334
  121. package/apps/mac/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -203
  122. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -280
  123. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -422
  124. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -94
  125. package/apps/mac/Sources/Core/Overlays/OverlayPanelShell.swift +0 -241
  126. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -3135
  127. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -3977
  128. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -119
  129. package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +0 -1217
  130. package/apps/mac/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +0 -1575
  131. package/apps/mac/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -148
  132. package/apps/mac/Sources/Core/Pi/PiAuthPromptCard.swift +0 -90
  133. package/apps/mac/Sources/Core/Pi/PiChatDock.swift +0 -564
  134. package/apps/mac/Sources/Core/Pi/PiChatSession.swift +0 -1948
  135. package/apps/mac/Sources/Core/Pi/PiInstallCallout.swift +0 -86
  136. package/apps/mac/Sources/Core/Pi/PiProviderSetupCallout.swift +0 -99
  137. package/apps/mac/Sources/Core/Pi/PiWorkspaceView.swift +0 -510
  138. package/apps/mac/Sources/Core/System/Capability.swift +0 -79
  139. package/apps/mac/Sources/Core/System/DiagnosticLog.swift +0 -373
  140. package/apps/mac/Sources/Core/System/EventBus.swift +0 -31
  141. package/apps/mac/Sources/Core/System/PermissionChecker.swift +0 -224
  142. package/apps/mac/Sources/Core/System/ProcessModel.swift +0 -199
  143. package/apps/mac/Sources/Core/System/ProcessQuery.swift +0 -151
  144. package/apps/mac/Sources/Core/System/SystemTelemetryMonitor.swift +0 -273
  145. package/apps/mac/Sources/Core/Voice/AdvisorLearningStore.swift +0 -90
  146. package/apps/mac/Sources/Core/Voice/AgentSession.swift +0 -377
  147. package/apps/mac/Sources/Core/Voice/AudioProvider.swift +0 -555
  148. package/apps/mac/Sources/Core/Voice/HandsOffSession.swift +0 -839
  149. package/apps/mac/Sources/Core/Voice/VoiceChatView.swift +0 -192
  150. package/apps/mac/Sources/Core/Voice/VoxClient.swift +0 -454
  151. package/apps/mac/Sources/Core/Workspace/Project.swift +0 -28
  152. package/apps/mac/Sources/Core/Workspace/ProjectScanner.swift +0 -141
  153. package/apps/mac/Sources/Core/Workspace/SessionLayerStore.swift +0 -285
  154. package/apps/mac/Sources/Core/Workspace/SessionManager.swift +0 -75
  155. package/apps/mac/Sources/Core/Workspace/Terminal/Terminal.swift +0 -259
  156. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -156
  157. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -200
  158. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -60
  159. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -105
  160. package/apps/mac/Sources/Core/Workspace/WorkspaceManager.swift +0 -1027
  161. package/apps/mac/Sources/UI/ActionRow.swift +0 -78
  162. package/apps/mac/Sources/UI/OrphanRow.swift +0 -129
  163. package/apps/mac/Sources/UI/ProjectRow.swift +0 -368
  164. package/apps/mac/Sources/UI/TabGroupRow.swift +0 -178
  165. package/apps/mac/Sources/UI/Theme.swift +0 -164
  166. package/apps/mac/Tests/StageDragTests.swift +0 -333
  167. package/apps/mac/Tests/StageJoinTests.swift +0 -313
  168. package/apps/mac/Tests/StageManagerTests.swift +0 -280
  169. package/apps/mac/Tests/StageTileTests.swift +0 -353
  170. package/swift/Package.swift +0 -20
  171. package/swift/Sources/DeckKit/DeckAction.swift +0 -51
  172. package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +0 -152
  173. package/swift/Sources/DeckKit/DeckCockpit.swift +0 -82
  174. package/swift/Sources/DeckKit/DeckHost.swift +0 -7
  175. package/swift/Sources/DeckKit/DeckManifest.swift +0 -145
  176. package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +0 -533
  177. package/swift/Sources/DeckKit/DeckTrackpad.swift +0 -63
  178. package/swift/Sources/DeckKit/DeckValue.swift +0 -93
  179. package/swift/Sources/DeckKit/DeckVoiceError.swift +0 -88
  180. package/swift/Tests/DeckKitTests/DeckKitTests.swift +0 -286
@@ -1,713 +0,0 @@
1
- import AppKit
2
- import Foundation
3
- import NaturalLanguage
4
-
5
- private struct IntentCandidate {
6
- let intent: IntentDef
7
- let slots: [String: JSON]
8
- let score: Double
9
- let semanticScore: Double
10
- let keywordBoost: Double
11
- let slotBoost: Double
12
- let matchedExample: String
13
- }
14
-
15
- private struct ExtractedSlots {
16
- let slots: [String: JSON]
17
- let boost: Double
18
- }
19
-
20
- final class VoiceIntentResolver {
21
- static let shared = VoiceIntentResolver()
22
-
23
- private let embedding = NLEmbedding.sentenceEmbedding(for: .english)
24
-
25
- private init() {}
26
-
27
- func match(text: String) -> IntentMatch? {
28
- let input = normalizeUtterance(text)
29
- guard !input.isEmpty else { return nil }
30
-
31
- if let direct = directMatch(for: input) {
32
- return direct
33
- }
34
-
35
- var candidates = IntentEngine.shared.definitions().compactMap { candidate(for: $0, input: input) }
36
- candidates.sort { lhs, rhs in
37
- if lhs.score == rhs.score {
38
- return lhs.semanticScore > rhs.semanticScore
39
- }
40
- return lhs.score > rhs.score
41
- }
42
-
43
- guard let best = candidates.first else { return nil }
44
-
45
- let minimumScore = best.intent.slots.contains(where: \.required) ? 0.42 : 0.36
46
- guard best.score >= minimumScore else { return nil }
47
-
48
- if let runnerUp = candidates.dropFirst().first,
49
- best.score - runnerUp.score < 0.05,
50
- best.keywordBoost < 0.18,
51
- best.slotBoost < 0.12 {
52
- return nil
53
- }
54
-
55
- return IntentMatch(
56
- intentName: best.intent.name,
57
- slots: best.slots,
58
- confidence: min(0.98, max(0.35, best.score)),
59
- matchedPhrase: best.matchedExample
60
- )
61
- }
62
-
63
- func execute(_ match: IntentMatch) throws -> JSON {
64
- try IntentEngine.shared.execute(IntentRequest(
65
- intent: match.intentName,
66
- slots: match.slots,
67
- rawText: nil,
68
- confidence: match.confidence,
69
- source: "voice-local"
70
- ))
71
- }
72
-
73
- func catalog() -> JSON {
74
- IntentEngine.shared.catalog()
75
- }
76
-
77
- private func candidate(for intent: IntentDef, input: String) -> IntentCandidate? {
78
- let extracted = extractSlots(for: intent.name, input: input)
79
- let requiredMissing = intent.slots.contains { $0.required && extracted.slots[$0.name] == nil }
80
- let exampleMatch = bestExampleMatch(for: intent, input: input)
81
- let keywordBoost = keywordBoost(for: intent.name, input: input)
82
- let exactBoost = normalizeUtterance(exampleMatch.example) == input ? 0.20 : 0.0
83
- let missingPenalty = requiredMissing ? 0.22 : 0.0
84
- let score = exampleMatch.score + keywordBoost + extracted.boost + exactBoost - missingPenalty
85
-
86
- if score <= 0 {
87
- return nil
88
- }
89
-
90
- return IntentCandidate(
91
- intent: intent,
92
- slots: extracted.slots,
93
- score: score,
94
- semanticScore: exampleMatch.score,
95
- keywordBoost: keywordBoost,
96
- slotBoost: extracted.boost,
97
- matchedExample: exampleMatch.example
98
- )
99
- }
100
-
101
- private func bestExampleMatch(for intent: IntentDef, input: String) -> (example: String, score: Double) {
102
- let examples = intent.examples + supplementalExamples[intent.name, default: []]
103
- guard !examples.isEmpty else { return ("", 0) }
104
-
105
- let best = examples
106
- .map { example -> (String, Double) in
107
- let normalizedExample = normalizeUtterance(example)
108
- if normalizedExample == input {
109
- return (example, 0.62)
110
- }
111
-
112
- let distance = semanticDistance(between: input, and: normalizedExample)
113
- let semantic = max(0, 1.18 - distance) * 0.48
114
- let overlap = tokenOverlap(input, normalizedExample) * 0.24
115
- return (example, semantic + overlap)
116
- }
117
- .max { $0.1 < $1.1 } ?? ("", 0)
118
-
119
- return best
120
- }
121
-
122
- private func semanticDistance(between lhs: String, and rhs: String) -> Double {
123
- guard let embedding else {
124
- return lhs == rhs ? 0 : 2
125
- }
126
- return Double(embedding.distance(between: lhs, and: rhs))
127
- }
128
-
129
- private func keywordBoost(for intentName: String, input: String) -> Double {
130
- guard let keywords = intentKeywords[intentName] else { return 0 }
131
-
132
- var boost = 0.0
133
- for keyword in keywords {
134
- if input.contains(keyword) {
135
- boost += keyword.contains(" ") ? 0.12 : 0.08
136
- }
137
- }
138
- return min(boost, 0.28)
139
- }
140
-
141
- private func extractSlots(for intentName: String, input: String) -> ExtractedSlots {
142
- switch intentName {
143
- case "tile_window":
144
- var slots: [String: JSON] = [:]
145
- var boost = 0.0
146
-
147
- if let position = resolvePosition(in: input) {
148
- slots["position"] = .string(position)
149
- boost += 0.28
150
- } else {
151
- return ExtractedSlots(slots: [:], boost: 0)
152
- }
153
-
154
- if refersToSelection(in: input) {
155
- slots["selection"] = .bool(true)
156
- boost += 0.08
157
- }
158
-
159
- return ExtractedSlots(slots: slots, boost: boost)
160
-
161
- case "distribute":
162
- var slots: [String: JSON] = [:]
163
- var boost = 0.0
164
-
165
- if let region = resolvePosition(in: input) {
166
- slots["region"] = .string(region)
167
- boost += 0.18
168
- }
169
-
170
- if let app = detectKnownApp(in: input) {
171
- slots["app"] = .string(app)
172
- boost += 0.14
173
- }
174
-
175
- if refersToSelection(in: input) {
176
- slots["selection"] = .bool(true)
177
- boost += 0.12
178
- }
179
-
180
- return ExtractedSlots(slots: slots, boost: boost)
181
-
182
- case "focus":
183
- if let app = detectKnownApp(in: input) ?? extractEntity(in: input, prefixes: focusPrefixes) {
184
- let resolved = resolveApp(app)
185
- guard !resolved.isEmpty else { return ExtractedSlots(slots: [:], boost: 0) }
186
- return ExtractedSlots(slots: ["app": .string(resolved)], boost: 0.18)
187
- }
188
- return ExtractedSlots(slots: [:], boost: 0)
189
-
190
- case "launch":
191
- if let project = extractEntity(in: input, prefixes: launchPrefixes) {
192
- let cleaned = cleanEntity(project)
193
- guard !cleaned.isEmpty else { return ExtractedSlots(slots: [:], boost: 0) }
194
- return ExtractedSlots(slots: ["project": .string(cleaned)], boost: 0.16)
195
- }
196
- return ExtractedSlots(slots: [:], boost: 0)
197
-
198
- case "switch_layer":
199
- if let layer = extractLayer(in: input) {
200
- guard !layer.isEmpty else { return ExtractedSlots(slots: [:], boost: 0) }
201
- return ExtractedSlots(slots: ["layer": .string(layer)], boost: 0.18)
202
- }
203
- return ExtractedSlots(slots: [:], boost: 0)
204
-
205
- case "search":
206
- if let query = extractSearchQuery(from: input) {
207
- let cleaned = cleanQuery(query)
208
- guard !cleaned.isEmpty else { return ExtractedSlots(slots: [:], boost: 0) }
209
- return ExtractedSlots(slots: ["query": .string(cleaned)], boost: 0.16)
210
- }
211
- return ExtractedSlots(slots: [:], boost: 0)
212
-
213
- case "create_layer":
214
- if let name = extractLayerName(from: input) {
215
- let cleaned = cleanEntity(name)
216
- guard !cleaned.isEmpty else { return ExtractedSlots(slots: [:], boost: 0) }
217
- return ExtractedSlots(slots: ["name": .string(cleaned)], boost: 0.14)
218
- }
219
- return ExtractedSlots(slots: [:], boost: 0)
220
-
221
- case "kill":
222
- if let session = extractEntity(in: input, prefixes: killPrefixes) {
223
- let cleaned = cleanEntity(session)
224
- guard !cleaned.isEmpty else { return ExtractedSlots(slots: [:], boost: 0) }
225
- return ExtractedSlots(slots: ["session": .string(cleaned)], boost: 0.16)
226
- }
227
- return ExtractedSlots(slots: [:], boost: 0)
228
-
229
- default:
230
- return ExtractedSlots(slots: [:], boost: 0)
231
- }
232
- }
233
-
234
- private func extractSearchQuery(from input: String) -> String? {
235
- let prefixes = [
236
- "find all the ", "find all ", "find ",
237
- "search for all ", "search for ", "search ",
238
- "look for ", "look up ", "locate all ", "locate ",
239
- "where is ", "where s ", "where does it say ", "where did i see ",
240
- "which window has ", "which window shows ",
241
- "help me find ", "can you find ",
242
- "show me all the ", "show me all ", "show all the ", "show all ",
243
- "open up all the ", "open up all ", "open all the ", "open all ",
244
- "pull up everything with ", "pull up all ", "pull up ",
245
- "bring up all the ", "bring up all ", "bring up my ",
246
- "where d my ", "i lost my ", "i lost ", "where the hell is ",
247
- "see all my ", "see all ", "see where s ", "see where is "
248
- ]
249
-
250
- if let entity = extractEntity(in: input, prefixes: prefixes) {
251
- return entity
252
- }
253
-
254
- if input.hasSuffix(" windows") || input.hasSuffix(" window") {
255
- return cleanQuery(input)
256
- }
257
-
258
- let wordCount = input.split(separator: " ").count
259
- if wordCount <= 3, !genericNonCommandPhrases.contains(input) {
260
- return input
261
- }
262
-
263
- return nil
264
- }
265
-
266
- private func extractLayerName(from input: String) -> String? {
267
- if let called = extractEntity(in: input, prefixes: [
268
- "create a layer called ", "create layer called ", "make a layer called ",
269
- "make layer called ", "new layer called ", "name this layer "
270
- ]) {
271
- return called
272
- }
273
-
274
- return extractEntity(in: input, prefixes: [
275
- "save this layout as ", "save layout as ", "save as layer ", "save as ",
276
- "create a layer ", "create layer ", "create new layer ",
277
- "make a layer ", "make layer ", "make a new layer ", "new layer "
278
- ])
279
- }
280
-
281
- private func extractLayer(in input: String) -> String? {
282
- if input == "next layer" || input == "previous layer" {
283
- return input
284
- }
285
-
286
- if let literal = [
287
- "layer one": "1",
288
- "layer two": "2",
289
- "layer three": "3",
290
- "first layer": "1",
291
- "second layer": "2",
292
- "third layer": "3",
293
- ][input] {
294
- return literal
295
- }
296
-
297
- if let entity = extractEntity(in: input, prefixes: [
298
- "switch to layer ", "switch to the ", "switch to ",
299
- "go to layer ", "go to the ", "go to ",
300
- "activate layer ", "activate the ", "change to layer ",
301
- "change layer to ", "layer "
302
- ]) {
303
- return cleanLayer(entity)
304
- }
305
-
306
- return nil
307
- }
308
-
309
- private func extractEntity(in input: String, prefixes: [String]) -> String? {
310
- for prefix in prefixes.sorted(by: { $0.count > $1.count }) {
311
- if input.hasPrefix(prefix) {
312
- return String(input.dropFirst(prefix.count))
313
- }
314
- }
315
- return nil
316
- }
317
-
318
- private func normalizeUtterance(_ text: String) -> String {
319
- var input = text.lowercased()
320
- .replacingOccurrences(of: #"[^\w\s-]"#, with: " ", options: .regularExpression)
321
- .split(separator: " ").joined(separator: " ")
322
- .trimmingCharacters(in: .whitespacesAndNewlines)
323
-
324
- var changed = true
325
- while changed {
326
- changed = false
327
- for prefix in leadingNoise {
328
- if input.hasPrefix(prefix) {
329
- input = String(input.dropFirst(prefix.count)).trimmingCharacters(in: .whitespaces)
330
- changed = true
331
- break
332
- }
333
- }
334
- }
335
-
336
- var stripped = true
337
- while stripped {
338
- stripped = false
339
- for suffix in trailingNoise {
340
- if input.hasSuffix(suffix) {
341
- input = String(input.dropLast(suffix.count)).trimmingCharacters(in: .whitespaces)
342
- stripped = true
343
- break
344
- }
345
- }
346
- }
347
-
348
- return input
349
- }
350
-
351
- private func resolvePosition(in input: String) -> String? {
352
- let map: [(keywords: [String], position: String)] = [
353
- (["top left", "upper left", "top-left"], "top-left"),
354
- (["top right", "upper right", "top-right"], "top-right"),
355
- (["bottom left", "lower left", "bottom-left"], "bottom-left"),
356
- (["bottom right", "lower right", "bottom-right"], "bottom-right"),
357
- (["left half", "left side", "the left", "left"], "left"),
358
- (["right half", "right side", "the right", "right"], "right"),
359
- (["maximize", "full screen", "full", "big", "max"], "maximize"),
360
- (["center", "middle", "centre"], "center"),
361
- (["top half", "top"], "top"),
362
- (["bottom half", "bottom"], "bottom"),
363
- ]
364
-
365
- for entry in map {
366
- if entry.keywords.contains(where: input.contains) {
367
- return entry.position
368
- }
369
- }
370
- return nil
371
- }
372
-
373
- private func directMatch(for input: String) -> IntentMatch? {
374
- if let intent = exactIntentMatches[input] {
375
- return IntentMatch(intentName: intent, slots: [:], confidence: 0.99, matchedPhrase: input)
376
- }
377
-
378
- if let query = exactSearchQuery(for: input) {
379
- return IntentMatch(
380
- intentName: "search",
381
- slots: ["query": .string(query)],
382
- confidence: 0.98,
383
- matchedPhrase: input
384
- )
385
- }
386
-
387
- if let app = exactFocusApp(for: input) {
388
- return IntentMatch(
389
- intentName: "focus",
390
- slots: ["app": .string(app)],
391
- confidence: 0.98,
392
- matchedPhrase: input
393
- )
394
- }
395
-
396
- if let position = exactTilePosition(for: input) {
397
- return IntentMatch(
398
- intentName: "tile_window",
399
- slots: ["position": .string(position)],
400
- confidence: 0.99,
401
- matchedPhrase: input
402
- )
403
- }
404
-
405
- if let session = exactKillSession(for: input) {
406
- return IntentMatch(
407
- intentName: "kill",
408
- slots: ["session": .string(session)],
409
- confidence: 0.98,
410
- matchedPhrase: input
411
- )
412
- }
413
-
414
- return nil
415
- }
416
-
417
- private func exactSearchQuery(for input: String) -> String? {
418
- if let query = extractSearchQuery(from: input), !query.isEmpty,
419
- searchPrefixes.contains(where: input.hasPrefix) {
420
- return cleanQuery(query)
421
- }
422
-
423
- if input.hasSuffix(" windows"), input != "list windows" {
424
- let query = cleanQuery(input)
425
- return query.isEmpty ? nil : query
426
- }
427
-
428
- return nil
429
- }
430
-
431
- private func exactFocusApp(for input: String) -> String? {
432
- if input.hasPrefix("see "), let app = detectKnownApp(in: input) ?? extractEntity(in: input, prefixes: ["see "]) {
433
- let resolved = resolveApp(app)
434
- return resolved.isEmpty ? nil : resolved
435
- }
436
-
437
- if input.hasSuffix(" on screen"), input.hasPrefix("get "),
438
- let app = extractEntity(in: input, prefixes: ["get "]) {
439
- let resolved = resolveApp(cleanEntity(app.replacingOccurrences(of: " on screen", with: "")))
440
- return resolved.isEmpty ? nil : resolved
441
- }
442
-
443
- return nil
444
- }
445
-
446
- private func exactTilePosition(for input: String) -> String? {
447
- if input == "right side" { return "right" }
448
- if input == "left side" { return "left" }
449
- return nil
450
- }
451
-
452
- private func exactKillSession(for input: String) -> String? {
453
- if input.hasPrefix("kill "), let session = extractEntity(in: input, prefixes: ["kill "]) {
454
- return session
455
- }
456
- if input.hasPrefix("stop "), let session = extractEntity(in: input, prefixes: ["stop "]) {
457
- return session
458
- }
459
- return nil
460
- }
461
-
462
- private func refersToSelection(in input: String) -> Bool {
463
- let markers = [
464
- "grid that", "grid these", "grid those",
465
- "tile that", "tile these", "tile those",
466
- "selected windows", "selection", "selected", "these windows", "those windows", "them"
467
- ]
468
- return markers.contains(where: input.contains)
469
- }
470
-
471
- private func detectKnownApp(in input: String) -> String? {
472
- for app in knownApps() {
473
- let lower = app.lowercased()
474
- if input.contains(lower) {
475
- return app
476
- }
477
- }
478
- return nil
479
- }
480
-
481
- private func knownApps() -> [String] {
482
- var names = Set(DesktopModel.shared.windows.values.map(\.app))
483
- for app in NSWorkspace.shared.runningApplications {
484
- if let name = app.localizedName, !name.isEmpty {
485
- names.insert(name)
486
- }
487
- }
488
- return names.sorted { $0.count > $1.count }
489
- }
490
-
491
- private func resolveApp(_ raw: String) -> String {
492
- let trimmed = cleanEntity(raw)
493
- let knownAliases: [String: String] = [
494
- "visual studio code": "Visual Studio Code",
495
- "vs code": "Visual Studio Code",
496
- "vscode": "Visual Studio Code",
497
- "google chrome": "Google Chrome",
498
- "iterm2": "iTerm2",
499
- "iterm": "iTerm2",
500
- ]
501
-
502
- if let alias = knownAliases[trimmed.lowercased()] {
503
- return alias
504
- }
505
-
506
- return trimmed
507
- .split(separator: " ")
508
- .map { $0.prefix(1).uppercased() + $0.dropFirst() }
509
- .joined(separator: " ")
510
- }
511
-
512
- private func cleanLayer(_ raw: String) -> String {
513
- cleanEntity(raw)
514
- .replacingOccurrences(of: " layer", with: "")
515
- .trimmingCharacters(in: .whitespaces)
516
- }
517
-
518
- private func cleanEntity(_ raw: String) -> String {
519
- var value = raw.trimmingCharacters(in: .whitespacesAndNewlines)
520
- let leading = ["the ", "my ", "a ", "an ", "this ", "that ", "like ", "um ", "uh "]
521
- let trailing = [
522
- " please", " for me", " right now", " real quick", " quickly",
523
- " session", " project", " app", " windows", " window", " layer"
524
- ]
525
-
526
- var changed = true
527
- while changed {
528
- changed = false
529
-
530
- for prefix in leading {
531
- if value.hasPrefix(prefix) {
532
- value = String(value.dropFirst(prefix.count)).trimmingCharacters(in: .whitespaces)
533
- changed = true
534
- }
535
- }
536
-
537
- for suffix in trailing {
538
- if value.hasSuffix(suffix) {
539
- value = String(value.dropLast(suffix.count)).trimmingCharacters(in: .whitespaces)
540
- changed = true
541
- }
542
- }
543
- }
544
-
545
- return value.trimmingCharacters(in: .whitespacesAndNewlines)
546
- }
547
-
548
- private func cleanQuery(_ raw: String) -> String {
549
- var query = raw
550
- let noise = [
551
- "all instances of ", "all of the ", "all the ", "all ",
552
- "instances of ", "of the ",
553
- "that mentioned ", "that mention ", "that say ", "that says ",
554
- "that have ", "that has ", "that are ", "that is ",
555
- "everything with ", "everything that has ",
556
- "windows ", "window ", "windows", "window",
557
- "stuff ", "stuff", "project ", "project", "app ", "app",
558
- "with the name ", "named ", "called ",
559
- "in the title", "in my title", "in the name", "on my screen", "on screen",
560
- " in it", " in there", " at", " go"
561
- ]
562
-
563
- for item in noise {
564
- query = query.replacingOccurrences(of: item, with: " ")
565
- }
566
-
567
- return query
568
- .split(separator: " ")
569
- .joined(separator: " ")
570
- .trimmingCharacters(in: .whitespacesAndNewlines)
571
- }
572
-
573
- private func tokenOverlap(_ lhs: String, _ rhs: String) -> Double {
574
- let lhsTokens = Set(lhs.split(separator: " ").map(String.init))
575
- let rhsTokens = Set(rhs.split(separator: " ").map(String.init))
576
- guard !lhsTokens.isEmpty, !rhsTokens.isEmpty else { return 0 }
577
- let intersection = lhsTokens.intersection(rhsTokens).count
578
- let union = lhsTokens.union(rhsTokens).count
579
- return Double(intersection) / Double(union)
580
- }
581
-
582
- private let leadingNoise = [
583
- "alright let s go ahead and ", "alright let s go ahead ", "let s go ahead and ",
584
- "alright let s ", "all right let s ",
585
- "okay let s ", "ok let s ",
586
- "can you please ", "could you please ", "would you please ",
587
- "i think i want to ", "i think i need to ",
588
- "can you ", "could you ", "would you ", "will you ",
589
- "i want to ", "i d like to ", "i would like to ",
590
- "i want you to ", "i need to ", "i need you to ",
591
- "i wanna ", "i think ", "i need ",
592
- "let s ", "let me ", "please ", "go ahead and ", "just ", "now ",
593
- "alright ", "all right ",
594
- "no sorry ", "sorry ", "no wait ", "wait ",
595
- "actually ", "okay ", "ok ",
596
- "um ", "uh ", "like ", "hmm ", "yeah ", "hey ", "yo ", "so "
597
- ]
598
-
599
- private let trailingNoise = [
600
- " please", " for me", " real quick", " right now", " quickly",
601
- " if you can", " when you get a chance", " at", " up"
602
- ]
603
-
604
- private let focusPrefixes = [
605
- "show me the ", "show me ", "show ", "focus on ", "focus the ", "focus ",
606
- "switch over to ", "switch to ", "go back to ", "go to ",
607
- "bring up the ", "bring up ", "bring forward ", "raise the ", "raise ",
608
- "pull up the ", "pull up ", "i want to see ", "let me see ",
609
- "take me to ", "give me the ", "give me ", "activate the ", "activate ",
610
- "jump to ", "can i get ", "see "
611
- ]
612
-
613
- private let launchPrefixes = [
614
- "open up ", "open my ", "open the ", "open ",
615
- "launch the ", "launch my ", "launch ",
616
- "start working on ", "start up ", "start the ", "start my ", "start ",
617
- "work on the ", "work on ",
618
- "begin working on ", "begin ",
619
- "fire up the ", "fire up ", "spin up ", "boot up ",
620
- "load up ", "load ", "run the ", "run "
621
- ]
622
-
623
- private let killPrefixes = [
624
- "kill the ", "kill ", "stop the ", "stop ",
625
- "shut down the ", "shut down ", "close the ", "close ",
626
- "terminate the ", "terminate ", "end the ", "end "
627
- ]
628
-
629
- private let genericNonCommandPhrases: Set<String> = [
630
- "what time is it",
631
- "tell me a joke",
632
- "how are you doing",
633
- "the weather today",
634
- "play some music",
635
- "set a timer for five minutes"
636
- ]
637
-
638
- private let intentKeywords: [String: [String]] = [
639
- "tile_window": ["tile", "snap", "move", "put", "throw", "left", "right", "top", "bottom", "center", "maximize", "full screen"],
640
- "focus": ["show", "focus", "switch to", "go to", "bring up", "pull up", "activate", "jump to"],
641
- "launch": ["open", "launch", "start", "begin", "fire up", "boot up", "work on", "run"],
642
- "switch_layer": ["layer", "switch to", "next layer", "previous layer"],
643
- "search": ["find", "search", "look for", "where is", "where d", "locate", "lost", "show me all", "windows"],
644
- "list_windows": ["what s open", "list windows", "which windows", "what do i have open"],
645
- "list_sessions": ["list sessions", "what s running", "which projects", "show my sessions"],
646
- "distribute": ["distribute", "spread", "organize", "arrange", "tidy", "clean up", "grid", "selected", "selection"],
647
- "create_layer": ["create layer", "save layout", "snapshot", "remember this layout"],
648
- "kill": ["kill", "stop", "shut down", "close", "terminate", "end"],
649
- "scan": ["scan", "rescan", "ocr", "read the screen", "what s on my screen", "screen text"],
650
- "help": ["help", "what can i do", "what can you do", "commands", "options"]
651
- ]
652
-
653
- private let searchPrefixes = [
654
- "find all the ", "find all ", "find ",
655
- "search for all ", "search for ", "search ",
656
- "look for ", "look up ", "locate all ", "locate ",
657
- "where is ", "where s ", "where does it say ", "where did i see ",
658
- "which window has ", "which window shows ",
659
- "help me find ", "can you find ",
660
- "show me all the ", "show me all ", "show all the ", "show all ",
661
- "open up all the ", "open up all ", "open all the ", "open all ",
662
- "pull up everything with ", "pull up all ", "pull up ",
663
- "bring up all the ", "bring up all ", "bring up my ",
664
- "where d my ", "i lost my ", "i lost ", "where the hell is ",
665
- "see all my ", "see all "
666
- ]
667
-
668
- private let exactIntentMatches: [String: String] = [
669
- "help": "help",
670
- "help me": "help",
671
- "what can i do": "help",
672
- "what can you do": "help",
673
- "how does this work": "help",
674
- "what can i say": "help",
675
- "what are my options": "help",
676
- "show me the commands": "help",
677
- "list windows": "list_windows",
678
- "what windows do i have": "list_windows",
679
- "list sessions": "list_sessions",
680
- "show me my sessions": "list_sessions",
681
- "rescan": "scan",
682
- "do a scan": "scan",
683
- "do a quick scan": "scan",
684
- "scan the screen": "scan",
685
- "read the screen": "scan",
686
- "refresh the screen text": "scan",
687
- "what s on my screen": "scan",
688
- "what s on the screen": "scan",
689
- "show me what s on the screen": "scan",
690
- "organize": "distribute",
691
- "organize my windows": "distribute",
692
- "line everything up": "distribute",
693
- "let s get everything organized": "distribute",
694
- "get everything organized": "distribute",
695
- "clean up the windows": "distribute",
696
- "tidy up": "distribute"
697
- ]
698
-
699
- private let supplementalExamples: [String: [String]] = [
700
- "tile_window": ["put this on the left side", "move this over to the right", "maximize", "center it"],
701
- "focus": ["i need to see chrome", "can i get safari up", "show me visual studio code"],
702
- "launch": ["fire up vox", "start working on lattices", "open my notes app"],
703
- "switch_layer": ["next layer", "previous layer", "switch to review"],
704
- "search": ["where d my slack go", "pull up everything with dewey in it", "show me all the chrome windows", "dewey"],
705
- "list_windows": ["what do i have open", "what windows do i have"],
706
- "list_sessions": ["show me my sessions", "which projects are active"],
707
- "distribute": ["tidy up", "line everything up", "clean up the windows", "grid that in the bottom half", "arrange the selected windows"],
708
- "create_layer": ["snapshot this", "remember this layout"],
709
- "kill": ["close the dewey session", "stop my session"],
710
- "scan": ["what s on my screen", "read the screen", "give me a fresh scan"],
711
- "help": ["what can i say", "show me the commands"]
712
- ]
713
- }