-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat(clients): bring AEGIS Omega App Intents package under version control #242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Tarik Skalić (tarikskalic33)
wants to merge
3
commits into
main
Choose a base branch
from
feat/app-intents-under-version-control
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
7f81293
feat(clients): bring AEGIS Omega App Intents package under version co…
tarikskalic33 2884ed4
chore(manifest): refresh cognitive-state anchors
github-actions[bot] f05304e
fix(app-intents): address review findings on router access and test d…
tarikskalic33 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // swift-tools-version: 5.9 | ||
|
|
||
| import PackageDescription | ||
|
|
||
| let package = Package( | ||
| name: "AEGISOmegaAppIntents", | ||
| defaultLocalization: "en", | ||
| platforms: [ | ||
| .iOS(.v17), | ||
| .macOS(.v14), | ||
| ], | ||
| products: [ | ||
| .library( | ||
| name: "AEGISOmegaAppIntents", | ||
| targets: ["AEGISOmegaAppIntents"] | ||
| ), | ||
| ], | ||
| targets: [ | ||
| .target( | ||
| name: "AEGISOmegaAppIntents" | ||
| ), | ||
| .testTarget( | ||
| name: "AEGISOmegaAppIntentsTests", | ||
| dependencies: ["AEGISOmegaAppIntents"] | ||
| ), | ||
| ] | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # AEGIS Omega — App Intents client | ||
|
|
||
| Siri / Spotlight / Shortcuts entry points that hand a single system-facing route into the app's | ||
| root scene. | ||
|
|
||
| ## Why this is in the repository | ||
|
|
||
| It was previously stranded in a transient agent scratch directory | ||
| (`Documents\Codex\2026-07-28\build-ios-apps-ios-app-intents-2\outputs\`). That is the same class | ||
| of location where the "holon-gram compiler" (8 files, +4446/−889) was lost permanently when its | ||
| container was reclaimed — see `CLAUDE.md` § *Verified Failure Modes* V1. Work that only exists in | ||
| an agent's working directory is one cleanup away from gone. | ||
|
|
||
| ## Layout | ||
|
|
||
| | File | Platform | Purpose | | ||
| |---|---|---| | ||
| | `AEGISIntentRoute.swift` | any | `AEGISIntentRoute` / `AEGISSessionMode` — pure `Foundation` route model | | ||
| | `AEGISIntentRouter.swift` | any | `@MainActor @Observable` singleton the root scene observes | | ||
| | `AEGISAppIntents.swift` | Apple only | `AppIntent` conformances + `AEGISSessionModeIntentValue` | | ||
| | `AEGISAppShortcuts.swift` | Apple only | `AppShortcutsProvider` phrases | | ||
|
|
||
| ## Cross-platform guard | ||
|
|
||
| `AppIntents` ships only in Apple SDKs. The two Apple-only sources are wrapped in | ||
| `#if canImport(AppIntents)`, and the test that exercises `AEGISSessionModeIntentValue` is guarded | ||
| the same way. On Apple platforms everything compiles unchanged; on Linux/Windows the pure | ||
| `Foundation` core and its tests still build, so the routing logic stays continuously verifiable | ||
| without a Mac. | ||
|
|
||
| ## Build | ||
|
|
||
| ```bash | ||
| swift build | ||
| swift test | ||
| ``` | ||
|
|
||
| **Windows prerequisite:** the Swift toolchain requires MSVC's `link.exe`. Without the Visual | ||
| Studio C++ workload, `swift build` fails with `toolchain is invalid: could not find CLI tool | ||
| 'link'`. Installing that workload requires an **elevated** process — a non-elevated | ||
| `setup.exe --quiet` exits `5007` and silently does nothing. | ||
|
|
||
| **iOS builds require macOS + Xcode.** No Windows toolchain can produce an iOS app; on Windows only | ||
| the cross-platform core and its tests are exercised. | ||
|
|
||
| ## Tests | ||
|
|
||
| `AEGISIntentRouteTests` covers route equality by payload, `AEGISSessionMode` raw-value round trip | ||
| (including the rejection of an unknown value), that `accept` replaces the pending handoff with a | ||
| fresh identity, and that `clear` on a superseded handoff does not drop the current one. Router | ||
| tests reset shared state via `defer`, since `AEGISIntentRouter.shared` is a singleton. |
105 changes: 105 additions & 0 deletions
105
clients/aegis-omega-app-intents/Sources/AEGISOmegaAppIntents/AEGISAppIntents.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| // AppIntents ships only in Apple SDKs. Guarded so the pure-Swift core and its | ||
| // tests still build on Linux/Windows; on Apple platforms this compiles unchanged. | ||
| #if canImport(AppIntents) | ||
| import AppIntents | ||
|
|
||
| public enum AEGISSessionModeIntentValue: String, AppEnum { | ||
| case focused | ||
| case governed | ||
| case recovery | ||
|
|
||
| public static let typeDisplayRepresentation: TypeDisplayRepresentation = "Session mode" | ||
|
|
||
| public static let caseDisplayRepresentations: [Self: DisplayRepresentation] = [ | ||
| .focused: "Focused", | ||
| .governed: "Governed", | ||
| .recovery: "Recovery", | ||
| ] | ||
|
|
||
| var domainValue: AEGISSessionMode { | ||
| switch self { | ||
| case .focused: .focused | ||
| case .governed: .governed | ||
| case .recovery: .recovery | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public struct ContinueContextIntent: AppIntent { | ||
| public static let title: LocalizedStringResource = "Continue context" | ||
| public static let description = IntentDescription( | ||
| "Open AEGIS Omega with context captured from Siri, Spotlight, or another shortcut." | ||
| ) | ||
| public static let openAppWhenRun = true | ||
|
|
||
| @Parameter( | ||
| title: "Context", | ||
| inputConnectionBehavior: .connectToPreviousIntentResult | ||
| ) | ||
| public var context: String? | ||
|
|
||
| public init() {} | ||
|
|
||
| public init(context: String?) { | ||
| self.context = context | ||
| } | ||
|
|
||
| public func perform() async throws -> some IntentResult { | ||
| await AEGISIntentRouter.shared.accept(.continueContext(text: context)) | ||
| return .result() | ||
| } | ||
| } | ||
|
|
||
| public struct StartGovernedSessionIntent: AppIntent { | ||
| public static let title: LocalizedStringResource = "Start governed session" | ||
| public static let description = IntentDescription( | ||
| "Open AEGIS Omega ready to begin a focused, governed, or recovery session." | ||
| ) | ||
| public static let openAppWhenRun = true | ||
|
|
||
| @Parameter(title: "Objective") | ||
| public var objective: String? | ||
|
|
||
| @Parameter(title: "Mode", default: .governed) | ||
| public var mode: AEGISSessionModeIntentValue | ||
|
|
||
| public init() {} | ||
|
|
||
| public init(objective: String?, mode: AEGISSessionModeIntentValue = .governed) { | ||
| self.objective = objective | ||
| self.mode = mode | ||
| } | ||
|
|
||
| public func perform() async throws -> some IntentResult { | ||
| await AEGISIntentRouter.shared.accept( | ||
| .startSession(objective: objective, mode: mode.domainValue) | ||
| ) | ||
| return .result() | ||
| } | ||
| } | ||
|
|
||
| public struct InspectEvidenceIntent: AppIntent { | ||
| public static let title: LocalizedStringResource = "Inspect evidence" | ||
| public static let description = IntentDescription( | ||
| "Open AEGIS Omega at the evidence surface, optionally focused on a reference." | ||
| ) | ||
| public static let openAppWhenRun = true | ||
|
|
||
| @Parameter( | ||
| title: "Reference", | ||
| inputConnectionBehavior: .connectToPreviousIntentResult | ||
| ) | ||
| public var reference: String? | ||
|
|
||
| public init() {} | ||
|
|
||
| public init(reference: String?) { | ||
| self.reference = reference | ||
| } | ||
|
|
||
| public func perform() async throws -> some IntentResult { | ||
| await AEGISIntentRouter.shared.accept(.inspectEvidence(reference: reference)) | ||
| return .result() | ||
| } | ||
| } | ||
| #endif |
18 changes: 18 additions & 0 deletions
18
clients/aegis-omega-app-intents/Sources/AEGISOmegaAppIntents/AEGISAppIntentsPackage.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Intent definitions that live in a Swift package (rather than the app bundle) are not | ||
| // indexed by Siri / Spotlight / Shortcuts unless the app declares the package. Providing | ||
| // this type lets the app target opt in: | ||
| // | ||
| // struct MyApp: App, AppIntentsPackage { | ||
| // static var includedPackages: [any AppIntentsPackage.Type] { | ||
| // [AEGISOmegaAppIntentsPackage.self] | ||
| // } | ||
| // } | ||
| // | ||
| // AppIntents ships only in Apple SDKs, so this is guarded like the other intent sources. | ||
| #if canImport(AppIntents) | ||
| import AppIntents | ||
|
|
||
| public struct AEGISOmegaAppIntentsPackage: AppIntentsPackage { | ||
| public init() {} | ||
| } | ||
| #endif |
39 changes: 39 additions & 0 deletions
39
clients/aegis-omega-app-intents/Sources/AEGISOmegaAppIntents/AEGISAppShortcuts.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // AppIntents ships only in Apple SDKs. Guarded so the pure-Swift core and its | ||
| // tests still build on Linux/Windows; on Apple platforms this compiles unchanged. | ||
| #if canImport(AppIntents) | ||
| import AppIntents | ||
|
|
||
| public struct AEGISAppShortcuts: AppShortcutsProvider { | ||
| public static var appShortcuts: [AppShortcut] { | ||
| AppShortcut( | ||
| intent: ContinueContextIntent(), | ||
| phrases: [ | ||
| "Continue context in \(.applicationName)", | ||
| "Remember this with \(.applicationName)", | ||
| ], | ||
| shortTitle: "Continue context", | ||
| systemImageName: "brain.head.profile" | ||
| ) | ||
|
|
||
| AppShortcut( | ||
| intent: StartGovernedSessionIntent(), | ||
| phrases: [ | ||
| "Start a governed session in \(.applicationName)", | ||
| "Begin a session with \(.applicationName)", | ||
| ], | ||
| shortTitle: "Start session", | ||
| systemImageName: "point.3.connected.trianglepath.dotted" | ||
| ) | ||
|
|
||
| AppShortcut( | ||
| intent: InspectEvidenceIntent(), | ||
| phrases: [ | ||
| "Inspect evidence in \(.applicationName)", | ||
| "Open evidence with \(.applicationName)", | ||
| ], | ||
| shortTitle: "Inspect evidence", | ||
| systemImageName: "checkmark.seal" | ||
| ) | ||
| } | ||
| } | ||
| #endif | ||
14 changes: 14 additions & 0 deletions
14
clients/aegis-omega-app-intents/Sources/AEGISOmegaAppIntents/AEGISIntentRoute.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import Foundation | ||
|
|
||
| /// The single system-facing route passed from App Intents into the main scene. | ||
| public enum AEGISIntentRoute: Equatable, Sendable { | ||
| case continueContext(text: String?) | ||
| case startSession(objective: String?, mode: AEGISSessionMode) | ||
| case inspectEvidence(reference: String?) | ||
| } | ||
|
|
||
| public enum AEGISSessionMode: String, CaseIterable, Codable, Sendable { | ||
| case focused | ||
| case governed | ||
| case recovery | ||
| } |
34 changes: 34 additions & 0 deletions
34
clients/aegis-omega-app-intents/Sources/AEGISOmegaAppIntents/AEGISIntentRouter.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import Foundation | ||
| import Observation | ||
|
|
||
| /// Main-actor handoff point observed by the app's root scene. | ||
| @MainActor | ||
| @Observable | ||
| public final class AEGISIntentRouter { | ||
| public struct Handoff: Identifiable, Equatable, Sendable { | ||
| public let id: UUID | ||
| public let route: AEGISIntentRoute | ||
|
|
||
| public init(id: UUID = UUID(), route: AEGISIntentRoute) { | ||
| self.id = id | ||
| self.route = route | ||
| } | ||
| } | ||
|
|
||
| public static let shared = AEGISIntentRouter() | ||
|
|
||
| /// Read-only to callers: mutation must go through `accept(_:)` / `clear(_:)` so the | ||
| /// id-matching guard in `clear(_:)` cannot be bypassed by an external write. | ||
| public private(set) var pendingHandoff: Handoff? | ||
|
|
||
| private init() {} | ||
|
|
||
| public func accept(_ route: AEGISIntentRoute) { | ||
| pendingHandoff = Handoff(route: route) | ||
| } | ||
|
|
||
| public func clear(_ handoff: Handoff) { | ||
| guard pendingHandoff?.id == handoff.id else { return } | ||
| pendingHandoff = nil | ||
| } | ||
| } |
85 changes: 85 additions & 0 deletions
85
clients/aegis-omega-app-intents/Tests/AEGISOmegaAppIntentsTests/AEGISIntentRouteTests.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import XCTest | ||
| @testable import AEGISOmegaAppIntents | ||
|
|
||
| // Every test method here is nonisolated and `async`, and the main-actor hop happens inside | ||
| // the body. SwiftPM builds its test-discovery list out of unapplied method references, so a | ||
| // method's isolation leaks into that list's element type: `@MainActor` methods produce | ||
| // `(Self) -> @MainActor () -> Void`, which off-Apple fails to compile when mixed with | ||
| // nonisolated methods and fails to *cast* at run time when it isn't. Keeping the whole class | ||
| // nonisolated keeps the element type uniform and castable, so the tests are actually invoked. | ||
| final class AEGISIntentRouteTests: XCTestCase { | ||
|
|
||
| /// Returns the router to a known-empty state through its public API only, | ||
| /// since `pendingHandoff` is read-only to callers. | ||
| @MainActor | ||
| private func drain(_ router: AEGISIntentRouter) { | ||
| while let h = router.pendingHandoff { router.clear(h) } | ||
| } | ||
|
|
||
| // Exercises the AppIntents-facing enum, so it can only run where AppIntents exists. | ||
| #if canImport(AppIntents) | ||
| func testSessionModesMapWithoutLosingMeaning() async { | ||
| XCTAssertEqual(AEGISSessionModeIntentValue.focused.domainValue, .focused) | ||
| XCTAssertEqual(AEGISSessionModeIntentValue.governed.domainValue, .governed) | ||
| XCTAssertEqual(AEGISSessionModeIntentValue.recovery.domainValue, .recovery) | ||
| } | ||
| #endif | ||
|
|
||
| func testRouterReplacesThePendingHandoffAtomically() async { | ||
| await MainActor.run { | ||
| let router = AEGISIntentRouter.shared | ||
| defer { drain(router) } | ||
|
|
||
| router.accept(.continueContext(text: "first")) | ||
| let first = router.pendingHandoff | ||
|
|
||
| router.accept(.inspectEvidence(reference: "receipt-42")) | ||
| let second = router.pendingHandoff | ||
|
|
||
| XCTAssertNotEqual(first?.id, second?.id) | ||
| XCTAssertEqual(second?.route, .inspectEvidence(reference: "receipt-42")) | ||
| } | ||
| } | ||
|
|
||
| // The route model is pure Foundation, so these run on every platform. | ||
| func testRouteEqualityDistinguishesPayloads() async { | ||
| XCTAssertEqual( | ||
| AEGISIntentRoute.continueContext(text: "a"), | ||
| AEGISIntentRoute.continueContext(text: "a") | ||
| ) | ||
| XCTAssertNotEqual( | ||
| AEGISIntentRoute.continueContext(text: "a"), | ||
| AEGISIntentRoute.continueContext(text: "b") | ||
| ) | ||
| XCTAssertNotEqual( | ||
| AEGISIntentRoute.startSession(objective: nil, mode: .focused), | ||
| AEGISIntentRoute.startSession(objective: nil, mode: .governed) | ||
| ) | ||
| } | ||
|
|
||
| func testSessionModeRoundTripsThroughItsRawValue() async { | ||
| for mode in AEGISSessionMode.allCases { | ||
| XCTAssertEqual(AEGISSessionMode(rawValue: mode.rawValue), mode) | ||
| } | ||
| XCTAssertNil(AEGISSessionMode(rawValue: "not-a-mode")) | ||
| } | ||
|
|
||
| func testClearOnlyDropsTheMatchingHandoff() async { | ||
| await MainActor.run { | ||
| let router = AEGISIntentRouter.shared | ||
| defer { drain(router) } | ||
|
|
||
| router.accept(.startSession(objective: "audit", mode: .recovery)) | ||
| guard let stale = router.pendingHandoff else { return XCTFail("expected a handoff") } | ||
|
|
||
| router.accept(.inspectEvidence(reference: "receipt-7")) | ||
| router.clear(stale) | ||
|
|
||
| XCTAssertEqual( | ||
| router.pendingHandoff?.route, | ||
| .inspectEvidence(reference: "receipt-7"), | ||
| "clearing a superseded handoff must not drop the current one" | ||
| ) | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.