From 292cb4a5c5a07134753955526e4adfc04e66439f Mon Sep 17 00:00:00 2001 From: Gale W Date: Tue, 9 Jun 2026 20:38:47 -0400 Subject: [PATCH 1/5] appkit: add thread sidebar view --- Sources/ASBAppKit/ASBAppKitModule.swift | 7 - Sources/ASBAppKit/ASBThreadSidebarView.swift | 339 ++++++++++++++++++ .../ASBAppKitTests/ASBAppKitModuleTests.swift | 10 - .../ASBThreadSidebarViewTests.swift | 106 ++++++ .../presentation-ui-targets-plan.md | 5 + 5 files changed, 450 insertions(+), 17 deletions(-) delete mode 100644 Sources/ASBAppKit/ASBAppKitModule.swift create mode 100644 Sources/ASBAppKit/ASBThreadSidebarView.swift delete mode 100644 Tests/ASBAppKitTests/ASBAppKitModuleTests.swift create mode 100644 Tests/ASBAppKitTests/ASBThreadSidebarViewTests.swift diff --git a/Sources/ASBAppKit/ASBAppKitModule.swift b/Sources/ASBAppKit/ASBAppKitModule.swift deleted file mode 100644 index 6590566..0000000 --- a/Sources/ASBAppKit/ASBAppKitModule.swift +++ /dev/null @@ -1,7 +0,0 @@ -import AppKit -import ASBPresentation -import SwiftASB - -struct ASBAppKitModule: Sendable { - static let name = "ASBAppKit" -} diff --git a/Sources/ASBAppKit/ASBThreadSidebarView.swift b/Sources/ASBAppKit/ASBThreadSidebarView.swift new file mode 100644 index 0000000..dd7b527 --- /dev/null +++ b/Sources/ASBAppKit/ASBThreadSidebarView.swift @@ -0,0 +1,339 @@ +import AppKit +import ASBPresentation + +/// AppKit sidebar view backed by a framework-neutral thread-sidebar snapshot. +@MainActor +public final class ASBThreadSidebarView: NSView { + public typealias IntentHandler = @MainActor (ThreadSidebarIntent) -> Void + + public private(set) var snapshot: ThreadSidebarSnapshot + public var onIntent: IntentHandler? + + private let scrollView: NSScrollView + private let outlineView: NSOutlineView + private let adapter = ASBThreadSidebarAdapter() + private var suppressSelectionIntent = false + + public init( + snapshot: ThreadSidebarSnapshot = .init(), + onIntent: IntentHandler? = nil + ) { + self.snapshot = snapshot + self.onIntent = onIntent + self.scrollView = NSScrollView() + self.outlineView = NSOutlineView() + super.init(frame: .zero) + configureViewHierarchy() + apply(snapshot) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("ASBThreadSidebarView does not support Interface Builder initialization.") + } + + public func apply(_ snapshot: ThreadSidebarSnapshot) { + self.snapshot = snapshot + adapter.apply(snapshot) + + suppressSelectionIntent = true + outlineView.reloadData() + restoreExpandedSections() + restoreSelection() + suppressSelectionIntent = false + } + + public func refreshUnarchivedThreads() { + onIntent?(.refreshUnarchivedThreads) + } + + public func refreshArchivedThreads() { + onIntent?(.refreshArchivedThreads) + } + + public func refreshSelectedWorktreeGitStatus() { + onIntent?(.refreshSelectedWorktreeGitStatus) + } + + private func configureViewHierarchy() { + translatesAutoresizingMaskIntoConstraints = false + + outlineView.delegate = self + outlineView.dataSource = self + outlineView.headerView = nil + outlineView.rowSizeStyle = .default + outlineView.style = .sourceList + outlineView.usesAutomaticRowHeights = false + outlineView.target = self + outlineView.doubleAction = #selector(openSelectedThread) + + let column = NSTableColumn(identifier: ASBThreadSidebarAdapter.columnIdentifier) + column.resizingMask = .autoresizingMask + outlineView.addTableColumn(column) + outlineView.outlineTableColumn = column + + scrollView.translatesAutoresizingMaskIntoConstraints = false + scrollView.documentView = outlineView + scrollView.hasVerticalScroller = true + scrollView.hasHorizontalScroller = false + addSubview(scrollView) + + NSLayoutConstraint.activate([ + scrollView.leadingAnchor.constraint(equalTo: leadingAnchor), + scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), + scrollView.topAnchor.constraint(equalTo: topAnchor), + scrollView.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) + } + + private func restoreExpandedSections() { + for section in adapter.sections { + outlineView.expandItem(section) + } + } + + private func restoreSelection() { + guard let selectedThreadID = snapshot.selection.selectedThreadID, + let row = adapter.row(forThreadID: selectedThreadID) + else { + outlineView.deselectAll(nil) + return + } + + let rowIndex = outlineView.row(forItem: row) + guard rowIndex >= 0 else { return } + outlineView.selectRowIndexes(IndexSet(integer: rowIndex), byExtendingSelection: false) + } + + @objc private func openSelectedThread() { + let selectedRow = outlineView.selectedRow + guard selectedRow >= 0, + let row = outlineView.item(atRow: selectedRow) as? ASBThreadSidebarRow, + let item = row.threadItem + else { return } + onIntent?(.openThread(id: item.id)) + } +} + +@MainActor +extension ASBThreadSidebarView: NSOutlineViewDataSource { + public func outlineView( + _ outlineView: NSOutlineView, + numberOfChildrenOfItem item: Any? + ) -> Int { + adapter.numberOfChildren(of: item) + } + + public func outlineView( + _ outlineView: NSOutlineView, + child index: Int, + ofItem item: Any? + ) -> Any { + adapter.child(index, of: item) + } + + public func outlineView( + _ outlineView: NSOutlineView, + isItemExpandable item: Any + ) -> Bool { + adapter.isExpandable(item) + } +} + +@MainActor +extension ASBThreadSidebarView: NSOutlineViewDelegate { + public func outlineViewSelectionDidChange(_ notification: Notification) { + guard !suppressSelectionIntent else { return } + let selectedRow = outlineView.selectedRow + guard selectedRow >= 0, + let row = outlineView.item(atRow: selectedRow) as? ASBThreadSidebarRow, + let item = row.threadItem + else { + onIntent?(.selectThread(id: nil)) + return + } + onIntent?(.selectThread(id: item.id)) + } + + public func outlineView( + _ outlineView: NSOutlineView, + viewFor tableColumn: NSTableColumn?, + item: Any + ) -> NSView? { + guard let row = item as? ASBThreadSidebarRow else { return nil } + let cell = outlineView.makeView( + withIdentifier: ASBThreadSidebarRowView.identifier, + owner: self + ) as? ASBThreadSidebarRowView ?? ASBThreadSidebarRowView() + cell.configure(row) + return cell + } + + public func outlineView( + _ outlineView: NSOutlineView, + shouldSelectItem item: Any + ) -> Bool { + (item as? ASBThreadSidebarRow)?.threadItem != nil + } + + public func outlineView( + _ outlineView: NSOutlineView, + heightOfRowByItem item: Any + ) -> CGFloat { + guard let row = item as? ASBThreadSidebarRow else { return 28 } + switch row.kind { + case .section: + return 24 + case .thread: + return 44 + } + } +} + +/// Snapshot adapter that owns AppKit tree mechanics for the sidebar. +@MainActor +public final class ASBThreadSidebarAdapter { + public static let columnIdentifier = NSUserInterfaceItemIdentifier("ASBThreadSidebarColumn") + + public private(set) var sections: [ASBThreadSidebarRow] = [] + + public init(snapshot: ThreadSidebarSnapshot = .init()) { + apply(snapshot) + } + + public func apply(_ snapshot: ThreadSidebarSnapshot) { + sections = snapshot.sections.map(ASBThreadSidebarRow.init(section:)) + } + + public var flattenedRows: [ASBThreadSidebarRow] { + sections.flatMap { [$0] + $0.children } + } + + public func row(forThreadID threadID: String) -> ASBThreadSidebarRow? { + sections.lazy + .flatMap(\.children) + .first { $0.threadItem?.id == threadID } + } + + public func numberOfChildren(of item: Any?) -> Int { + guard let row = item as? ASBThreadSidebarRow else { + return sections.count + } + return row.children.count + } + + public func child(_ index: Int, of item: Any?) -> ASBThreadSidebarRow { + guard let row = item as? ASBThreadSidebarRow else { + return sections[index] + } + return row.children[index] + } + + public func isExpandable(_ item: Any) -> Bool { + guard let row = item as? ASBThreadSidebarRow else { return false } + return !row.children.isEmpty + } +} + +/// One AppKit outline row derived from a presentation snapshot. +@MainActor +public final class ASBThreadSidebarRow: NSObject, Identifiable { + public enum Kind: Sendable, Equatable { + case section + case thread + } + + public let id: String + public let kind: Kind + public let title: String + public let subtitle: String? + public let threadItem: ThreadSidebarItem? + public let children: [ASBThreadSidebarRow] + + public init(section: ThreadSidebarSection) { + self.id = "section:\(section.id)" + self.kind = .section + self.title = section.title + self.subtitle = nil + self.threadItem = nil + self.children = section.items.map(ASBThreadSidebarRow.init(item:)) + super.init() + } + + public init(item: ThreadSidebarItem) { + self.id = "thread:\(item.id)" + self.kind = .thread + self.title = item.title + self.subtitle = Self.subtitle(for: item) + self.threadItem = item + self.children = [] + super.init() + } + + private static func subtitle(for item: ThreadSidebarItem) -> String? { + if !item.preview.isEmpty { + return item.preview + } + if item.worktreeTitle != item.projectTitle { + return item.worktreeTitle + } + return nil + } +} + +private final class ASBThreadSidebarRowView: NSTableCellView { + static let identifier = NSUserInterfaceItemIdentifier("ASBThreadSidebarRowView") + + private let titleField = NSTextField(labelWithString: "") + private let subtitleField = NSTextField(labelWithString: "") + private let stackView = NSStackView() + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + identifier = Self.identifier + configureHierarchy() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("ASBThreadSidebarRowView does not support Interface Builder initialization.") + } + + func configure(_ row: ASBThreadSidebarRow) { + titleField.stringValue = row.title + subtitleField.stringValue = row.subtitle ?? "" + subtitleField.isHidden = row.subtitle?.isEmpty ?? true + titleField.font = row.kind == .section + ? NSFont.preferredFont(forTextStyle: .subheadline).withSymbolicTraits(.bold) + : NSFont.preferredFont(forTextStyle: .body) + } + + private func configureHierarchy() { + titleField.lineBreakMode = .byTruncatingTail + subtitleField.lineBreakMode = .byTruncatingTail + subtitleField.textColor = .secondaryLabelColor + subtitleField.font = .preferredFont(forTextStyle: .caption1) + + stackView.orientation = .vertical + stackView.alignment = .leading + stackView.spacing = 2 + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.addArrangedSubview(titleField) + stackView.addArrangedSubview(subtitleField) + + addSubview(stackView) + + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8), + stackView.centerYAnchor.constraint(equalTo: centerYAnchor), + ]) + } +} + +private extension NSFont { + func withSymbolicTraits(_ traits: NSFontDescriptor.SymbolicTraits) -> NSFont { + let descriptor = fontDescriptor.withSymbolicTraits(traits) + return NSFont(descriptor: descriptor, size: pointSize) ?? self + } +} diff --git a/Tests/ASBAppKitTests/ASBAppKitModuleTests.swift b/Tests/ASBAppKitTests/ASBAppKitModuleTests.swift deleted file mode 100644 index af4d06a..0000000 --- a/Tests/ASBAppKitTests/ASBAppKitModuleTests.swift +++ /dev/null @@ -1,10 +0,0 @@ -@testable import ASBAppKit -import Testing - -@Suite("ASBAppKit module") -struct ASBAppKitModuleTests { - @Test("module scaffold is available") - func moduleScaffoldIsAvailable() { - #expect(ASBAppKitModule.name == "ASBAppKit") - } -} diff --git a/Tests/ASBAppKitTests/ASBThreadSidebarViewTests.swift b/Tests/ASBAppKitTests/ASBThreadSidebarViewTests.swift new file mode 100644 index 0000000..c6df6bc --- /dev/null +++ b/Tests/ASBAppKitTests/ASBThreadSidebarViewTests.swift @@ -0,0 +1,106 @@ +@testable import ASBAppKit +import ASBPresentation +import Testing + +@Suite("ASBThreadSidebarView") +@MainActor +struct ASBThreadSidebarViewTests { + @Test("adapter maps presentation sections into outline rows") + func adapterBuildsOutlineRows() { + let adapter = ASBThreadSidebarAdapter(snapshot: snapshot()) + + #expect(adapter.sections.map(\.id) == ["section:recent", "section:archived"]) + #expect(adapter.numberOfChildren(of: nil) == 2) + #expect(adapter.numberOfChildren(of: adapter.sections[0]) == 2) + #expect(adapter.child(0, of: nil).title == "Recent") + #expect(adapter.child(1, of: adapter.sections[0]).threadItem?.id == "thread-2") + #expect(adapter.isExpandable(adapter.sections[0])) + #expect(!adapter.isExpandable(adapter.sections[0].children[0])) + } + + @Test("adapter preserves stable thread row identity") + func adapterFindsRowsByThreadID() { + let adapter = ASBThreadSidebarAdapter(snapshot: snapshot()) + + let row = adapter.row(forThreadID: "thread-2") + + #expect(row?.id == "thread:thread-2") + #expect(row?.title == "Second") + #expect(row?.threadItem?.worktreeID == "worktree-1") + } + + @Test("adapter updates row tree when snapshot changes") + func adapterReplacesRowsOnSnapshotUpdate() { + let adapter = ASBThreadSidebarAdapter(snapshot: snapshot()) + let updated = ThreadSidebarSnapshot( + sections: [ + .init(id: "recent", title: "Recent", items: [ + item(id: "thread-3", title: "Third"), + ]), + ], + selection: .init(selectedThreadID: "thread-3") + ) + + adapter.apply(updated) + + #expect(adapter.sections.map(\.id) == ["section:recent"]) + #expect(adapter.flattenedRows.map(\.id) == ["section:recent", "thread:thread-3"]) + #expect(adapter.row(forThreadID: "thread-1") == nil) + #expect(adapter.row(forThreadID: "thread-3")?.title == "Third") + } + + @Test("view exposes refresh commands as presentation intents") + func viewEmitsRefreshIntents() { + var intents: [ThreadSidebarIntent] = [] + let view = ASBThreadSidebarView(snapshot: snapshot()) { + intents.append($0) + } + + view.refreshUnarchivedThreads() + view.refreshArchivedThreads() + view.refreshSelectedWorktreeGitStatus() + + #expect(intents == [ + .refreshUnarchivedThreads, + .refreshArchivedThreads, + .refreshSelectedWorktreeGitStatus, + ]) + } + + private func snapshot() -> ThreadSidebarSnapshot { + ThreadSidebarSnapshot( + sections: [ + .init(id: "recent", title: "Recent", items: [ + item(id: "thread-1", title: "First", preview: "First preview"), + item(id: "thread-2", title: "Second", preview: "Second preview"), + ]), + .init(id: "archived", title: "Archived", items: [ + item(id: "thread-archived", title: "Archived", isArchived: true), + ]), + ], + selection: .init(selectedThreadID: "thread-2") + ) + } + + private func item( + id: String, + title: String, + preview: String = "", + isArchived: Bool = false + ) -> ThreadSidebarItem { + ThreadSidebarItem( + id: id, + title: title, + preview: preview, + sourceBadge: .cli, + activityStatus: .idle, + isArchived: isArchived, + projectID: "project-1", + projectTitle: "Project", + worktreeID: "worktree-1", + worktreeTitle: "Worktree", + repositoryID: "https://github.com/gaelic-ghost/SwiftASB", + updatedAt: 1 + ) + } +} diff --git a/docs/maintainers/presentation-ui-targets-plan.md b/docs/maintainers/presentation-ui-targets-plan.md index ea518d8..fe2c560 100644 --- a/docs/maintainers/presentation-ui-targets-plan.md +++ b/docs/maintainers/presentation-ui-targets-plan.md @@ -319,6 +319,11 @@ guessing chronology. - Emit typed presentation intents instead of mutating SwiftASB directly from view cells. +Status: complete. `ASBThreadSidebarView` consumes `ThreadSidebarSnapshot`, owns +the AppKit outline-view adapter, maps selection and open actions to +`ThreadSidebarIntent`, and keeps outline rows, item expansion, row heights, and +view reuse inside `ASBAppKit`. + ### Slice 5: ASBSwiftUI Sidebar Wrapper And Light Panels - Add the `ASBSwiftUI` target. From a99506633e6822847c34949db5223a815be936de Mon Sep 17 00:00:00 2001 From: Gale W Date: Tue, 9 Jun 2026 21:27:27 -0400 Subject: [PATCH 2/5] schema: refresh codex cli 0.138 wire --- README.md | 2 +- ROADMAP.md | 59 ++++-- .../CodexLifecycleV2Batch+JSONValue.swift | 170 ++++++++++++++---- .../CodexAppServer+CodexExtensions.swift | 4 +- .../Public/CodexAppServer+Compatibility.swift | 1 + .../Public/CodexAppServer+WireMapping.swift | 38 +--- .../CodexCLIExecutableResolver.swift | 2 +- .../CodexAppServerProtocolTests.swift | 9 +- .../CodexAppServerLiveIntegrationTests.swift | 2 +- .../Public/CodexAppServerTestSupport.swift | 1 + .../Public/CodexAppServerTests.swift | 20 +-- .../CodexCLIExecutableResolverTests.swift | 20 +-- Tools/AgentSB/tests/test_cli.py | 2 +- Tools/AgentSB/tests/test_tools.py | 2 +- .../2026-06-09-agentsb-maintenance-draft.md | 154 ++++++++++++++++ .../2026-06-09-agentsb-schema-review.md | 69 +++++++ .../interactive-lifecycle-release-boundary.md | 2 +- scripts/generate-wire-types.sh | 6 +- 18 files changed, 452 insertions(+), 111 deletions(-) create mode 100644 docs/agents/reports/2026-06-09-agentsb-maintenance-draft.md create mode 100644 docs/agents/reports/2026-06-09-agentsb-schema-review.md diff --git a/README.md b/README.md index c7d8550..4d041db 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Check your Codex version: ```bash codex --version ``` -*Note: SwiftASB currently supports the latest reviewed Codex CLI minor release, `0.137.x`. This narrow reviewed window will be revised once the app-server schema stabilizes or Codex CLI reaches a v1.x.x release.* +*Note: SwiftASB currently supports the latest reviewed Codex CLI minor release, `0.138.x`. This narrow reviewed window will be revised once the app-server schema stabilizes or Codex CLI reaches a v1.x.x release.* Add the Socket Marketplace to Codex and enable the SwiftASB Skills Plugin: diff --git a/ROADMAP.md b/ROADMAP.md index 04c9941..326a1cd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -42,7 +42,7 @@ | --- | --- | --- | | Bundled schema-driven wire generation | `Shipped internally` | `scripts/generate-wire-types.sh` derives from the bundled v2 schema, patches dynamic JSON to `CodexWireJSONValue`, and validates the staged Swift output. | | Promoted generated v2 wire snapshot | `Shipped internally` | `Sources/SwiftASB/Generated/CodexWire/Latest/` now contains a wider lifecycle batch covering bootstrap, stored and loaded thread reads, filesystem reads and watches, config reads, extension inventory, remote-control status plus pairing/client-management wire families, thread goals, and many thread, turn, item, reasoning, and tool-progress notifications, alongside the hand-owned `CodexWireInitializeResponse` shim. | -| Codex CLI schema review | `Shipped / ongoing` | The current reviewed compatibility window is `codex-cli 0.137.x`; the v0.137 schema families have been classified for the current boundary, and `scripts/dump-codex-schemas.sh` makes future versioned experimental dumps repeatable by default. Future Codex CLI schema families still need public/observable/internal decisions before promotion. | +| Codex CLI schema review | `Shipped / ongoing` | The current reviewed compatibility window is `codex-cli 0.138.x`; the v0.138 schema families have been classified for the current boundary, and `scripts/dump-codex-schemas.sh` makes future versioned experimental dumps repeatable by default. Future Codex CLI schema families still need public/observable/internal decisions before promotion. | | Stdio subprocess transport | `Shipped internally` | The transport launches `codex app-server --listen stdio://`, frames newline-delimited JSON, correlates request IDs, and captures stderr for diagnostics. | | Raw server-event fanout | `Shipped internally` | Transport can stream raw JSON-RPC notifications and server requests to higher layers. | | Typed protocol request encoding | `Shipped internally` | `initialize`, `initialized`, core thread and turn methods, archive-state actions, filesystem reads and watches, config reads, app/skill/plugin/collaboration-mode inventory, model/MCP/hook reads, MCP resource reads, and thread-goal methods are encoded through the protocol layer. | @@ -88,7 +88,7 @@ | Internal thread history persistence | `Partially shipped` | The package now has a Core Data-backed `ThreadHistoryStore` that persists live-built thread and turn history, hydrates stored turns from `thread/read`, `thread/resume`, `thread/fork`, and `thread/turns/list`, seeds previously unknown local threads from paged history, widens persisted turn identity to stay thread-scoped across forks, and records explicit fork lineage while preserving conservative reconciliation that keeps richer local detail when upstream stored history is thinner. Public history paging/search helpers and archive-retention policy are still open. | | Direct local Codex thread storage | `Planned / prototyped` | SwiftASB now has a maintainer plan for an opt-in, read-only local Codex storage reader that can inventory threads through Codex's SQLite metadata and lazily hydrate JSONL evidence without forcing every caller through the app-server JSONL pipe. AgentSB prototypes the inspection shape for reports only; the package API, version gates, privacy defaults, and fallback behavior remain separate SwiftASB design work. See [`docs/maintainers/codex-direct-thread-storage-plan.md`](docs/maintainers/codex-direct-thread-storage-plan.md). | | Convenience run API | `Not started` | No `run(...)` or one-shot text convenience layer yet. | -| Binary discovery and compatibility policy | `Partially shipped` | Explicit binary override exists, the docs now define a current-reviewed Codex CLI support window of `0.137.x`, transport startup checks PATH, common Homebrew paths, and the npm global prefix on macOS, and `cliExecutableDiagnostics()` now exposes the resolved binary, version string, and documented support-window assessment. Any further diagnostics work is now expansion rather than a missing baseline surface. | +| Binary discovery and compatibility policy | `Partially shipped` | Explicit binary override exists, the docs now define a current-reviewed Codex CLI support window of `0.138.x`, transport startup checks PATH, common Homebrew paths, and the npm global prefix on macOS, and `cliExecutableDiagnostics()` now exposes the resolved binary, version string, and documented support-window assessment. Any further diagnostics work is now expansion rather than a missing baseline surface. | | README-level consumer docs | `Shipped / ongoing` | The README covers installation, runtime assumptions, first-use examples, the supported lifecycle, SwiftUI companion surfaces, and the current Codex CLI compatibility window. Future README work should track new public API additions rather than prerelease readiness. | | AgentSB maintainer automation | `Report-first maintainer app` | `Tools/AgentSB/` is a repo-local Python maintainer app that inspects SwiftASB deterministically, writes tracked reports under `docs/agents/reports/`, evaluates safety-boundary cases, diffs schema dumps, writes reviewable maintenance drafts, and prototypes local Codex thread-index inspection for future SwiftASB planning. The v1 boundary stays report-first: safe auto-apply is classifier-gated and limited to AgentSB-owned report artifacts, and it must not mutate Swift source, generated wire snapshots, public API, releases, or behavior-changing docs. | | Agent workflow guidance | `Shipped / ongoing` | SwiftASB-specific Codex guidance now ships through `socket`'s [`swiftasb-skills`](https://github.com/gaelic-ghost/socket/tree/main/plugins/swiftasb-skills) plugin, with skills for explaining SwiftASB, choosing an integration shape, building SwiftUI-facing app state, and diagnosing integration failures. This repo now points package users and maintainers at that plugin while keeping SwiftASB source, DocC, tests, generated-wire review, and release notes here as the package source of truth. | @@ -113,14 +113,15 @@ or command-approval completion. Those slices now exist and shipped in the `v1.7.1` baseline. The next meaningful work is to probe and deliberately shape the newly promoted -Codex CLI `0.137.x` wire families before widening public API. Remote-control -pairing/client management, skills extra roots, richer turn-start context, -runtime workspace roots, environments, structured output schemas, and -rollout-path forking all need live behavior evidence before SwiftASB turns them -into stable Swift surfaces. Descriptors should compile against Codex-owned -workspace, Git, file, and thread facts wherever possible, rather than making -SwiftASB or a sandboxed client infer repository identity by walking the local -filesystem. +Codex CLI `0.138.x` wire families before widening public API. Remote-control +pairing/client management and pairing status, account token usage, turn +moderation metadata, plugin app templates, skills extra roots, richer +turn-start context, runtime workspace roots, environments, structured output +schemas, and rollout-path forking all need live behavior evidence before +SwiftASB turns them into stable Swift surfaces. Descriptors should compile +against Codex-owned workspace, Git, file, and thread facts wherever possible, +rather than making SwiftASB or a sandboxed client infer repository identity by +walking the local filesystem. The 2026-05-11 repository-wide security audit added two patch-sized hardening items that landed before broadening more protocol surface: preserving or @@ -278,11 +279,11 @@ consuming apps to adopt: ## Current Patch Release Slice -This slice records the `v1.7.1` compatibility patch. It does not widen the -public API boundary. Its job is to keep SwiftASB aligned with the latest -reviewed Codex CLI `0.137.x` app-server schema while preserving generated wire -families as internal scaffolding until their behavior and ownership model are -probed. +This slice records the `v1.7.1` and follow-up compatibility patches. It does +not widen the public API boundary. Its job is to keep SwiftASB aligned with the +latest reviewed Codex CLI `0.138.x` app-server schema while preserving generated +wire families as internal scaffolding until their behavior and ownership model +are probed. ### Shipped in v1.7.1 @@ -300,6 +301,12 @@ probed. forking probe-first instead of promising stable public request parameters in this patch. - [x] Exercise deterministic Swift and AgentSB tests before tagging the patch. +- [x] Classify the Codex CLI `v0.138.0` schema diff before promotion. + Decision: refresh the promoted v2 lifecycle batch, update the reviewed CLI + window to `0.138.x`, promote account token usage, remote-control pairing + status, and turn moderation metadata wire types internally, and keep those + schema families out of public API until their user-facing jobs and ownership + model are deliberately designed. ### Follow-Up Probes @@ -702,10 +709,16 @@ workflow earns them in a later feature release. and skills extra-roots wire types internally, and keep those request families as future public API decisions until their permission and ownership model is designed deliberately. +- [x] Classify the Codex CLI `v0.138.0` schema diff before promotion. + Decision: refresh the promoted v2 lifecycle batch, update the reviewed CLI + window to `0.138.x`, promote account token usage, remote-control pairing + status, turn moderation metadata, plugin app-template, encrypted-content, and + response-item author/recipient wire changes internally, and keep them out of + public API until live behavior and consumer use cases are clearer. - [x] Decide whether v1 should support only the latest documented rolling window or whether a shorter first-v1 compatibility promise is more honest. Decision: use a narrow latest-reviewed-minor support window, currently - `0.137.x`, and widen deliberately after generated-wire and public API review + `0.138.x`, and widen deliberately after generated-wire and public API review catches up with later Codex CLI releases. @@ -815,7 +828,7 @@ workflow earns them in a later feature release. #### Compatibility Window - The compatibility promise is intentionally narrow while app-server schema is - moving quickly: reviewed support for Codex CLI `0.137.x`. + moving quickly: reviewed support for Codex CLI `0.138.x`. - SwiftASB discovers `codex` from an explicit executable URL, `PATH`, common Homebrew locations, or the npm global prefix, and exposes startup diagnostics through `cliExecutableDiagnostics()`. @@ -1180,6 +1193,12 @@ not as the current maintainer priority. remote-control pairing/client-management plus skills extra-roots wire types internally while leaving public API ownership for those request families as future design work. +- A `v0.138.0` experimental schema compatibility pass has refreshed the staging + generator again, updated the Codex CLI compatibility window, and promoted + account token usage, remote-control pairing status, turn moderation metadata, + plugin app-template, encrypted-content, and response-item author/recipient + wire changes internally while leaving public API ownership for those families + as future design work. - API curation and DocC docs good enough that a Swift consumer can understand the supported package surface without reading maintainer notes, including walkthroughs for the primary v1 lifecycle jobs. @@ -1603,6 +1622,12 @@ Completed v0.137.0 generated wire snapshot internally, and kept new remote-control and skills extra-roots request families out of public API until their ownership model is designed. +- 2026-06-09: Used AgentSB schema check, dump, diff, and maintenance-draft + reports for the Codex CLI `0.138.x` compatibility refresh, promoted the + v0.138.0 generated wire snapshot internally, and kept account token usage, + remote-control pairing status, turn moderation metadata, plugin app-template, + encrypted-content, and response-item author/recipient changes out of public + API pending live behavior review. - 2026-06-05: Took AgentSB through an AI-enabled schema-report and auto-apply-safe shakedown, dumped local `codex-cli 0.137.0` schemas with the existing script, and recorded follow-up work for CLI ergonomics, installed diff --git a/Sources/SwiftASB/Generated/CodexWire/Latest/CodexLifecycleV2Batch+JSONValue.swift b/Sources/SwiftASB/Generated/CodexWire/Latest/CodexLifecycleV2Batch+JSONValue.swift index dbe10a4..774db2a 100644 --- a/Sources/SwiftASB/Generated/CodexWire/Latest/CodexLifecycleV2Batch+JSONValue.swift +++ b/Sources/SwiftASB/Generated/CodexWire/Latest/CodexLifecycleV2Batch+JSONValue.swift @@ -44,6 +44,7 @@ struct CodexWireCodexLifecycleV2Batch: Codable, Equatable, Sendable { let fsUnwatchResponse: [String: CodexWireJSONValue]? let fsWatchParams: CodexWireFSWatchParams? let fsWatchResponse: CodexWireFSWatchResponse? + let getAccountTokenUsageResponse: CodexWireGetAccountTokenUsageResponse? let guardianWarningNotification: CodexWireGuardianWarningNotification? let hookCompletedNotification: CodexWireHookCompletedNotification? let hookStartedNotification: CodexWireHookStartedNotification? @@ -96,6 +97,8 @@ struct CodexWireCodexLifecycleV2Batch: Codable, Equatable, Sendable { let remoteControlClientsRevokeResponse: [String: CodexWireJSONValue]? let remoteControlPairingStartParams: CodexWireRemoteControlPairingStartParams? let remoteControlPairingStartResponse: CodexWireRemoteControlPairingStartResponse? + let remoteControlPairingStatusParams: CodexWireRemoteControlPairingStatusParams? + let remoteControlPairingStatusResponse: CodexWireRemoteControlPairingStatusResponse? let remoteControlStatusChangedNotification: CodexWireRemoteControlStatusChangedNotification? let reviewStartParams: CodexWireReviewStartParams? let reviewStartResponse: CodexWireReviewStartResponse? @@ -148,6 +151,7 @@ struct CodexWireCodexLifecycleV2Batch: Codable, Equatable, Sendable { let threadUnarchiveResponse: CodexWireThreadUnarchiveResponse? let turnCompletedNotification: CodexWireTurnCompletedNotification? let turnDiffUpdatedNotification: CodexWireTurnDiffUpdatedNotification? + let turnModerationMetadataNotification: CodexWireTurnModerationMetadataNotification? let turnPlanUpdatedNotification: CodexWireTurnPlanUpdatedNotification? let turnStartedNotification: CodexWireTurnStartedNotification? let turnStartParams: CodexWireTurnStartParams? @@ -156,10 +160,10 @@ struct CodexWireCodexLifecycleV2Batch: Codable, Equatable, Sendable { let windowsSandboxReadinessResponse: CodexWireWindowsSandboxReadinessResponse? enum CodingKeys: String, CodingKey { - case agentMessageDeltaNotification, appListUpdatedNotification, appsListParams, appsListResponse, collaborationModeListParams, collaborationModeListResponse, commandExecOutputDeltaNotification, commandExecutionOutputDeltaNotification, configReadParams, configReadResponse, configRequirementsReadResponse, configWarningNotification, contextCompactedNotification, deprecationNoticeNotification, errorNotification, externalAgentConfigImportCompletedNotification, fileChangeOutputDeltaNotification, fileChangePatchUpdatedNotification, fsChangedNotification, fsGetMetadataParams, fsGetMetadataResponse, fsReadDirectoryParams, fsReadDirectoryResponse, fsReadFileParams, fsReadFileResponse, fsUnwatchParams, fsUnwatchResponse, fsWatchParams, fsWatchResponse, guardianWarningNotification, hookCompletedNotification, hookStartedNotification, initializeParams, itemCompletedNotification, itemGuardianApprovalReviewCompletedNotification, itemGuardianApprovalReviewStartedNotification, itemStartedNotification + case agentMessageDeltaNotification, appListUpdatedNotification, appsListParams, appsListResponse, collaborationModeListParams, collaborationModeListResponse, commandExecOutputDeltaNotification, commandExecutionOutputDeltaNotification, configReadParams, configReadResponse, configRequirementsReadResponse, configWarningNotification, contextCompactedNotification, deprecationNoticeNotification, errorNotification, externalAgentConfigImportCompletedNotification, fileChangeOutputDeltaNotification, fileChangePatchUpdatedNotification, fsChangedNotification, fsGetMetadataParams, fsGetMetadataResponse, fsReadDirectoryParams, fsReadDirectoryResponse, fsReadFileParams, fsReadFileResponse, fsUnwatchParams, fsUnwatchResponse, fsWatchParams, fsWatchResponse, getAccountTokenUsageResponse, guardianWarningNotification, hookCompletedNotification, hookStartedNotification, initializeParams, itemCompletedNotification, itemGuardianApprovalReviewCompletedNotification, itemGuardianApprovalReviewStartedNotification, itemStartedNotification case listMCPServerStatusParams = "listMcpServerStatusParams" case listMCPServerStatusResponse = "listMcpServerStatusResponse" - case mcpResourceReadParams, mcpResourceReadResponse, mcpServerStatusUpdatedNotification, mcpToolCallProgressNotification, modelListParams, modelListResponse, modelReroutedNotification, modelVerificationNotification, planDeltaNotification, pluginListParams, pluginListResponse, pluginReadParams, pluginReadResponse, pluginShareDeleteParams, pluginShareDeleteResponse, pluginShareListParams, pluginShareListResponse, pluginShareSaveParams, pluginShareSaveResponse, pluginShareUpdateTargetsParams, pluginShareUpdateTargetsResponse, pluginSkillReadParams, pluginSkillReadResponse, processExitedNotification, processKillParams, processKillResponse, processOutputDeltaNotification, processResizePtyParams, processResizePtyResponse, processSpawnParams, processSpawnResponse, processWriteStdinParams, processWriteStdinResponse, rawResponseItemCompletedNotification, reasoningSummaryPartAddedNotification, reasoningSummaryTextDeltaNotification, reasoningTextDeltaNotification, remoteControlClientsListParams, remoteControlClientsListResponse, remoteControlClientsRevokeParams, remoteControlClientsRevokeResponse, remoteControlPairingStartParams, remoteControlPairingStartResponse, remoteControlStatusChangedNotification, reviewStartParams, reviewStartResponse, serverRequestResolvedNotification, skillsChangedNotification, skillsExtraRootsSetParams, skillsExtraRootsSetResponse, skillsListParams, skillsListResponse, threadApproveGuardianDeniedActionParams, threadApproveGuardianDeniedActionResponse, threadArchivedNotification, threadArchiveParams, threadArchiveResponse, threadClosedNotification, threadCompactStartParams, threadCompactStartResponse, threadGoalClearedNotification, threadGoalClearParams, threadGoalClearResponse, threadGoalGetParams, threadGoalGetResponse, threadGoalSetParams, threadGoalSetResponse, threadGoalUpdatedNotification, threadLoadedListParams, threadLoadedListResponse, threadMetadataUpdateParams, threadMetadataUpdateResponse, threadNameUpdatedNotification, threadRollbackParams, threadRollbackResponse, threadSearchParams, threadSearchResponse, threadSetNameParams, threadSetNameResponse, threadShellCommandParams, threadShellCommandResponse, threadStartedNotification, threadStartParams, threadStartResponse, threadStatusChangedNotification, threadTokenUsageUpdatedNotification, threadTurnsItemsListParams, threadTurnsItemsListResponse, threadTurnsListParams, threadTurnsListResponse, threadUnarchivedNotification, threadUnarchiveParams, threadUnarchiveResponse, turnCompletedNotification, turnDiffUpdatedNotification, turnPlanUpdatedNotification, turnStartedNotification, turnStartParams, turnStartResponse, warningNotification, windowsSandboxReadinessResponse + case mcpResourceReadParams, mcpResourceReadResponse, mcpServerStatusUpdatedNotification, mcpToolCallProgressNotification, modelListParams, modelListResponse, modelReroutedNotification, modelVerificationNotification, planDeltaNotification, pluginListParams, pluginListResponse, pluginReadParams, pluginReadResponse, pluginShareDeleteParams, pluginShareDeleteResponse, pluginShareListParams, pluginShareListResponse, pluginShareSaveParams, pluginShareSaveResponse, pluginShareUpdateTargetsParams, pluginShareUpdateTargetsResponse, pluginSkillReadParams, pluginSkillReadResponse, processExitedNotification, processKillParams, processKillResponse, processOutputDeltaNotification, processResizePtyParams, processResizePtyResponse, processSpawnParams, processSpawnResponse, processWriteStdinParams, processWriteStdinResponse, rawResponseItemCompletedNotification, reasoningSummaryPartAddedNotification, reasoningSummaryTextDeltaNotification, reasoningTextDeltaNotification, remoteControlClientsListParams, remoteControlClientsListResponse, remoteControlClientsRevokeParams, remoteControlClientsRevokeResponse, remoteControlPairingStartParams, remoteControlPairingStartResponse, remoteControlPairingStatusParams, remoteControlPairingStatusResponse, remoteControlStatusChangedNotification, reviewStartParams, reviewStartResponse, serverRequestResolvedNotification, skillsChangedNotification, skillsExtraRootsSetParams, skillsExtraRootsSetResponse, skillsListParams, skillsListResponse, threadApproveGuardianDeniedActionParams, threadApproveGuardianDeniedActionResponse, threadArchivedNotification, threadArchiveParams, threadArchiveResponse, threadClosedNotification, threadCompactStartParams, threadCompactStartResponse, threadGoalClearedNotification, threadGoalClearParams, threadGoalClearResponse, threadGoalGetParams, threadGoalGetResponse, threadGoalSetParams, threadGoalSetResponse, threadGoalUpdatedNotification, threadLoadedListParams, threadLoadedListResponse, threadMetadataUpdateParams, threadMetadataUpdateResponse, threadNameUpdatedNotification, threadRollbackParams, threadRollbackResponse, threadSearchParams, threadSearchResponse, threadSetNameParams, threadSetNameResponse, threadShellCommandParams, threadShellCommandResponse, threadStartedNotification, threadStartParams, threadStartResponse, threadStatusChangedNotification, threadTokenUsageUpdatedNotification, threadTurnsItemsListParams, threadTurnsItemsListResponse, threadTurnsListParams, threadTurnsListResponse, threadUnarchivedNotification, threadUnarchiveParams, threadUnarchiveResponse, turnCompletedNotification, turnDiffUpdatedNotification, turnModerationMetadataNotification, turnPlanUpdatedNotification, turnStartedNotification, turnStartParams, turnStartResponse, warningNotification, windowsSandboxReadinessResponse } } @@ -357,7 +361,7 @@ struct CodexWireCollaborationModeMask: Codable, Equatable, Sendable { let mode: CodexWireModeKind? let model: String? let name: String - let reasoningEffort: CodexWireReasoningEffort? + let reasoningEffort: String? enum CodingKeys: String, CodingKey { case mode, model, name @@ -371,17 +375,6 @@ enum CodexWireModeKind: String, Codable, Equatable, Sendable { case plan = "plan" } -/// See -/// https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning -enum CodexWireReasoningEffort: String, Codable, Equatable, Sendable { - case high = "high" - case low = "low" - case medium = "medium" - case minimal = "minimal" - case none = "none" - case xhigh = "xhigh" -} - // // Hashable or Equatable: // The compiler will not be able to synthesize the implementation of Hashable or Equatable @@ -496,7 +489,7 @@ struct CodexWireConfig: Codable, Equatable, Sendable { let modelAutoCompactTokenLimitScope: CodexWireAutoCompactTokenLimitScope? let modelContextWindow: Int? let modelProvider: String? - let modelReasoningEffort: CodexWireReasoningEffort? + let modelReasoningEffort: String? let modelReasoningSummary: CodexWireReasoningSummary? let modelVerbosity: CodexWireVerbosity? let reviewModel: String? @@ -907,12 +900,13 @@ struct CodexWireConfigRequirements: Codable, Equatable, Sendable { let allowAppshots: Bool? let allowedApprovalPolicies: [CodexWireAskForApproval]? let allowedApprovalsReviewers: [CodexWireApprovalsReviewer]? - let allowedPermissions: [String]? + let allowedPermissionProfiles: [String: Bool]? let allowedSandboxModes: [CodexWireSandboxMode]? let allowedWebSearchModes: [CodexWireWebSearchMode]? let allowedWindowsSandboxImplementations: [CodexWireWindowsSandboxSetupMode]? let allowManagedHooksOnly: Bool? let computerUse: CodexWireComputerUseRequirements? + let defaultPermissions: String? let enforceResidency: CodexWireResidencyRequirement? let featureRequirements: [String: Bool]? let hooks: CodexWireManagedHooksRequirements? @@ -1568,6 +1562,48 @@ struct CodexWireFSWatchResponse: Codable, Equatable, Sendable { // for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be // synthesized for types that have collections (such as arrays or dictionaries). +// MARK: - CodexWireGetAccountTokenUsageResponse +struct CodexWireGetAccountTokenUsageResponse: Codable, Equatable, Sendable { + let dailyUsageBuckets: [CodexWireAccountTokenUsageDailyBucket]? + let summary: CodexWireAccountTokenUsageSummary +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + +// MARK: - CodexWireAccountTokenUsageDailyBucket +struct CodexWireAccountTokenUsageDailyBucket: Codable, Equatable, Sendable { + let startDate: String + let tokens: Int +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + +// MARK: - CodexWireAccountTokenUsageSummary +struct CodexWireAccountTokenUsageSummary: Codable, Equatable, Sendable { + let currentStreakDays, lifetimeTokens, longestRunningTurnSEC, longestStreakDays: Int? + let peakDailyTokens: Int? + + enum CodingKeys: String, CodingKey { + case currentStreakDays, lifetimeTokens + case longestRunningTurnSEC = "longestRunningTurnSec" + case longestStreakDays, peakDailyTokens + } +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + // MARK: - CodexWireGuardianWarningNotification struct CodexWireGuardianWarningNotification: Codable, Equatable, Sendable { /// Concise guardian warning message for the user. @@ -1844,7 +1880,7 @@ struct CodexWireThreadItem: Codable, Equatable, Sendable { /// Prompt text sent as part of the collab tool call, when available. let prompt: String? /// Reasoning effort requested for the spawned agent, when applicable. - let reasoningEffort: CodexWireReasoningEffort? + let reasoningEffort: String? /// Thread ID of the receiving agent, when applicable. In case of spawn operation, this /// corresponds to the newly spawned agent. let receiverThreadIDS: [String]? @@ -2781,7 +2817,7 @@ struct CodexWireModel: Codable, Equatable, Sendable { /// Deprecated: use `serviceTiers` instead. let additionalSpeedTiers: [String]? let availabilityNux: CodexWireModelAvailabilityNux? - let defaultReasoningEffort: CodexWireReasoningEffort + let defaultReasoningEffort: String /// Catalog default service tier id for this model, when one is configured. let defaultServiceTier: String? let description, displayName: String @@ -2838,7 +2874,7 @@ struct CodexWireModelServiceTier: Codable, Equatable, Sendable { // MARK: - CodexWireReasoningEffortOption struct CodexWireReasoningEffortOption: Codable, Equatable, Sendable { let description: String - let reasoningEffort: CodexWireReasoningEffort + let reasoningEffort: String } // @@ -3213,6 +3249,7 @@ struct CodexWirePluginReadResponse: Codable, Equatable, Sendable { // MARK: - CodexWirePluginDetail struct CodexWirePluginDetail: Codable, Equatable, Sendable { let apps: [CodexWireAppSummary] + let appTemplates: [CodexWireAppTemplateSummary] let description: String? let hooks: [CodexWirePluginHookSummary] let marketplaceName: String @@ -3228,6 +3265,36 @@ struct CodexWirePluginDetail: Codable, Equatable, Sendable { // for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be // synthesized for types that have collections (such as arrays or dictionaries). +// MARK: - CodexWireAppTemplateSummary +struct CodexWireAppTemplateSummary: Codable, Equatable, Sendable { + let canonicalConnectorID, description, logoURL, logoURLDark: String? + let materializedAppIDS: [String] + let name: String + let reason: CodexWireAppTemplateUnavailableReason? + let templateID: String + + enum CodingKeys: String, CodingKey { + case canonicalConnectorID = "canonicalConnectorId" + case description + case logoURL = "logoUrl" + case logoURLDark = "logoUrlDark" + case materializedAppIDS = "materializedAppIds" + case name, reason + case templateID = "templateId" + } +} + +enum CodexWireAppTemplateUnavailableReason: String, Codable, Equatable, Sendable { + case noActiveWorkspace = "NO_ACTIVE_WORKSPACE" + case notConfiguredForWorkspace = "NOT_CONFIGURED_FOR_WORKSPACE" +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + /// EXPERIMENTAL - app metadata summary for plugin responses. // MARK: - CodexWireAppSummary struct CodexWireAppSummary: Codable, Equatable, Sendable { @@ -3647,6 +3714,7 @@ struct CodexWireResponseItem: Codable, Equatable, Sendable { let phase: CodexWireMessagePhase? let role: String? let type: CodexWireResponseItemType + let author, recipient: String? let encryptedContent: String? let summary: [CodexWireReasoningItemReasoningSummary]? let action: CodexWireResponsesAPIWebSearchAction? @@ -3663,7 +3731,7 @@ struct CodexWireResponseItem: Codable, Equatable, Sendable { let revisedPrompt: String? enum CodingKeys: String, CodingKey { - case content, id, phase, role, type + case content, id, phase, role, type, author, recipient case encryptedContent = "encrypted_content" case summary, action case callID = "call_id" @@ -3714,17 +3782,19 @@ enum CodexWireExecLocalShellActionType: String, Codable, Equatable, Sendable { // MARK: - CodexWireContentItem struct CodexWireContentItem: Codable, Equatable, Sendable { let text: String? - let type: CodexWireType + let type: CodexWireEncryptedContentAgentMessageInputContentType let detail: CodexWireImageDetail? - let imageURL: String? + let imageURL, encryptedContent: String? enum CodingKeys: String, CodingKey { case text, type, detail case imageURL = "image_url" + case encryptedContent = "encrypted_content" } } -enum CodexWireType: String, Codable, Equatable, Sendable { +enum CodexWireEncryptedContentAgentMessageInputContentType: String, Codable, Equatable, Sendable { + case encryptedContent = "encrypted_content" case inputImage = "input_image" case inputText = "input_text" case outputText = "output_text" @@ -3805,6 +3875,7 @@ enum CodexWireSummaryTextReasoningItemReasoningSummaryType: String, Codable, Equ } enum CodexWireResponseItemType: String, Codable, Equatable, Sendable { + case agentMessage = "agent_message" case compaction = "compaction" case compactionTrigger = "compaction_trigger" case contextCompaction = "context_compaction" @@ -3992,6 +4063,28 @@ struct CodexWireRemoteControlPairingStartResponse: Codable, Equatable, Sendable // for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be // synthesized for types that have collections (such as arrays or dictionaries). +// MARK: - CodexWireRemoteControlPairingStatusParams +struct CodexWireRemoteControlPairingStatusParams: Codable, Equatable, Sendable { + let manualPairingCode, pairingCode: String? +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + +// MARK: - CodexWireRemoteControlPairingStatusResponse +struct CodexWireRemoteControlPairingStatusResponse: Codable, Equatable, Sendable { + let claimed: Bool +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + /// Current remote-control connection status and remote identity exposed to clients. // MARK: - CodexWireRemoteControlStatusChangedNotification struct CodexWireRemoteControlStatusChangedNotification: Codable, Equatable, Sendable { @@ -5038,8 +5131,7 @@ struct CodexWireThreadStartParams: Codable, Equatable, Sendable { /// Named profile id for this thread. Cannot be combined with `sandbox`. let permissions: String? let personality: CodexWirePersonality? - /// Replace the thread's runtime workspace roots. Relative paths are resolved against the - /// effective cwd for the thread. + /// Replace the thread's runtime workspace roots. Paths must be absolute. let runtimeWorkspaceRoots: [String]? let sandbox: CodexWireSandboxMode? let serviceName, serviceTier: String? @@ -5107,7 +5199,7 @@ struct CodexWireThreadStartResponse: Codable, Equatable, Sendable { /// Instruction source files currently loaded for this thread. let instructionSources: [String]? let model, modelProvider: String - let reasoningEffort: CodexWireReasoningEffort? + let reasoningEffort: String? /// Thread-scoped runtime workspace roots used to materialize `:workspace_roots`. let runtimeWorkspaceRoots: [String]? /// Legacy sandbox policy retained for compatibility. Experimental clients should prefer @@ -5426,6 +5518,24 @@ struct CodexWireTurnDiffUpdatedNotification: Codable, Equatable, Sendable { // for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be // synthesized for types that have collections (such as arrays or dictionaries). +// MARK: - CodexWireTurnModerationMetadataNotification +struct CodexWireTurnModerationMetadataNotification: Codable, Equatable, Sendable { + let metadata: CodexWireJSONValue + let threadID, turnID: String + + enum CodingKeys: String, CodingKey { + case metadata + case threadID = "threadId" + case turnID = "turnId" + } +} + +// +// Hashable or Equatable: +// The compiler will not be able to synthesize the implementation of Hashable or Equatable +// for types that require the use of CodexWireJSONValue, nor will the implementation of Hashable be +// synthesized for types that have collections (such as arrays or dictionaries). + // MARK: - CodexWireTurnPlanUpdatedNotification struct CodexWireTurnPlanUpdatedNotification: Codable, Equatable, Sendable { let explanation: String? @@ -5481,7 +5591,7 @@ struct CodexWireTurnStartParams: Codable, Equatable, Sendable { /// Override the working directory for this turn and subsequent turns. let cwd: String? /// Override the reasoning effort for this turn and subsequent turns. - let effort: CodexWireReasoningEffort? + let effort: String? /// Optional turn-scoped environments. /// /// Omitted uses the thread sticky environments. Empty disables environment access for this @@ -5500,8 +5610,8 @@ struct CodexWireTurnStartParams: Codable, Equatable, Sendable { let personality: CodexWirePersonality? /// Optional turn-scoped Responses API client metadata. let responsesapiClientMetadata: [String: String]? - /// Replace the thread's runtime workspace roots for this turn and subsequent turns. Relative - /// paths are resolved against the effective cwd for the turn. + /// Replace the thread's runtime workspace roots for this turn and subsequent turns. Paths + /// must be absolute. let runtimeWorkspaceRoots: [String]? /// Override the sandbox policy for this turn and subsequent turns. let sandboxPolicy: CodexWireDangerFullAccessSandboxPolicyClass? @@ -5560,7 +5670,7 @@ struct CodexWireCollaborationMode: Codable, Equatable, Sendable { struct CodexWireSettings: Codable, Equatable, Sendable { let developerInstructions: String? let model: String - let reasoningEffort: CodexWireReasoningEffort? + let reasoningEffort: String? enum CodingKeys: String, CodingKey { case developerInstructions = "developer_instructions" diff --git a/Sources/SwiftASB/Public/CodexAppServer+CodexExtensions.swift b/Sources/SwiftASB/Public/CodexAppServer+CodexExtensions.swift index f8cfdb6..e60aff6 100644 --- a/Sources/SwiftASB/Public/CodexAppServer+CodexExtensions.swift +++ b/Sources/SwiftASB/Public/CodexAppServer+CodexExtensions.swift @@ -848,7 +848,9 @@ extension CodexExtensions.CollaborationMode { kind: wireValue.mode.map(Kind.init), model: wireValue.model, name: wireValue.name, - reasoningEffort: wireValue.reasoningEffort.map(CodexAppServer.ReasoningEffort.init) + reasoningEffort: wireValue.reasoningEffort.map { + CodexAppServer.ReasoningEffort(wireValue: $0) + } ) } } diff --git a/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift b/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift index 09cebf6..2babe20 100644 --- a/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift +++ b/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift @@ -77,6 +77,7 @@ extension CodexAppServer { case medium case minimal case none + case unrecognized case xhigh } diff --git a/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift b/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift index 03cd4e7..0af0b06 100644 --- a/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift +++ b/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift @@ -487,43 +487,17 @@ extension CodexAppServer.SessionStartSource { } extension CodexAppServer.ReasoningEffort { - init(wireValue: CodexWireReasoningEffort) { - self = Self(wireValue: Optional(wireValue))! + init(wireValue: String) { + self = Self(rawValue: wireValue) ?? .unrecognized } - init?(wireValue: CodexWireReasoningEffort?) { + init?(wireValue: String?) { guard let wireValue else { return nil } - switch wireValue { - case .high: - self = .high - case .low: - self = .low - case .medium: - self = .medium - case .minimal: - self = .minimal - case .none: - self = .none - case .xhigh: - self = .xhigh - } + self.init(wireValue: wireValue) } - var wireValue: CodexWireReasoningEffort { - switch self { - case .high: - .high - case .low: - .low - case .medium: - .medium - case .minimal: - .minimal - case .none: - .none - case .xhigh: - .xhigh - } + var wireValue: String { + rawValue } } diff --git a/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift b/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift index 0ec1aa1..7a15384 100644 --- a/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift +++ b/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift @@ -30,7 +30,7 @@ internal struct CodexCLIExecutableResolver { internal let patch: Int private static let regex = try! NSRegularExpression(pattern: #"(\d+)\.(\d+)\.(\d+)"#) - internal static let latestSupportedPublicRelease = Version(major: 0, minor: 137, patch: 0) + internal static let latestSupportedPublicRelease = Version(major: 0, minor: 138, patch: 0) internal static var documentedWindowDescription: String { let latest = latestSupportedPublicRelease diff --git a/Tests/SwiftASBTests/Protocol/CodexAppServerProtocolTests.swift b/Tests/SwiftASBTests/Protocol/CodexAppServerProtocolTests.swift index 9cc04d2..09a72d6 100644 --- a/Tests/SwiftASBTests/Protocol/CodexAppServerProtocolTests.swift +++ b/Tests/SwiftASBTests/Protocol/CodexAppServerProtocolTests.swift @@ -911,11 +911,11 @@ struct CodexAppServerProtocolTests { settings: .init( developerInstructions: nil, model: "gpt-5.4", - reasoningEffort: .medium + reasoningEffort: "medium" ) ), cwd: "/tmp/project", - effort: .medium, + effort: "medium", environments: nil, input: [ CodexWireUserInput( @@ -964,8 +964,9 @@ struct CodexAppServerProtocolTests { let input = try #require(params["input"] as? [[String: Any]]) #expect(input.count == 1) - #expect(input.first?["type"] as? String == "text") - #expect(input.first?["text"] as? String == "Hello from SwiftASB") + let inputRow = try #require(input.first) + #expect(inputRow["type"] as? String == "text") + #expect(inputRow["text"] as? String == "Hello from SwiftASB") let outputSchema = try #require(params["outputSchema"] as? [String: Any]) #expect(outputSchema["type"] as? String == "object") diff --git a/Tests/SwiftASBTests/Public/CodexAppServerLiveIntegrationTests.swift b/Tests/SwiftASBTests/Public/CodexAppServerLiveIntegrationTests.swift index 9388dbe..9fc61f3 100644 --- a/Tests/SwiftASBTests/Public/CodexAppServerLiveIntegrationTests.swift +++ b/Tests/SwiftASBTests/Public/CodexAppServerLiveIntegrationTests.swift @@ -200,7 +200,7 @@ struct CodexAppServerLiveIntegrationTests { let diagnostics = try await client.cliExecutableDiagnostics() #expect(diagnostics.resolvedExecutablePath == harness.codexExecutableURL.path) #expect(diagnostics.versionString.contains("codex-cli")) - #expect(diagnostics.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(diagnostics.compatibility == .supported(documentedWindow: "0.138.x")) await client.stop() } catch { diff --git a/Tests/SwiftASBTests/Public/CodexAppServerTestSupport.swift b/Tests/SwiftASBTests/Public/CodexAppServerTestSupport.swift index b8965e7..d740838 100644 --- a/Tests/SwiftASBTests/Public/CodexAppServerTestSupport.swift +++ b/Tests/SwiftASBTests/Public/CodexAppServerTestSupport.swift @@ -908,6 +908,7 @@ actor FakeCodexAppServerTransport: CodexAppServerTransporting { "needsAuth": true, ], ], + "appTemplates": [], "description": "GitHub plugin detail fixture.", "hooks": [ [ diff --git a/Tests/SwiftASBTests/Public/CodexAppServerTests.swift b/Tests/SwiftASBTests/Public/CodexAppServerTests.swift index e3dbf7c..1f77360 100644 --- a/Tests/SwiftASBTests/Public/CodexAppServerTests.swift +++ b/Tests/SwiftASBTests/Public/CodexAppServerTests.swift @@ -83,8 +83,8 @@ struct CodexAppServerTests { launchArgumentsPrefix: [], resolvedExecutableURL: URL(fileURLWithPath: "/opt/homebrew/bin/codex"), source: .homebrewAppleSilicon, - versionString: "codex-cli 0.137.0", - compatibility: .supported(documentedWindow: "0.137.x") + versionString: "codex-cli 0.138.0", + compatibility: .supported(documentedWindow: "0.138.x") ) ) let client = CodexAppServer(transport: transport) @@ -94,8 +94,8 @@ struct CodexAppServerTests { let diagnostics = try await client.cliExecutableDiagnostics() #expect(diagnostics.source == .homebrewAppleSilicon) #expect(diagnostics.resolvedExecutablePath == "/opt/homebrew/bin/codex") - #expect(diagnostics.versionString == "codex-cli 0.137.0") - #expect(diagnostics.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(diagnostics.versionString == "codex-cli 0.138.0") + #expect(diagnostics.compatibility == .supported(documentedWindow: "0.138.x")) await client.stop() } @@ -108,8 +108,8 @@ struct CodexAppServerTests { launchArgumentsPrefix: [], resolvedExecutableURL: URL(fileURLWithPath: "/opt/homebrew/bin/codex"), source: .homebrewAppleSilicon, - versionString: "codex-cli 0.137.0", - compatibility: .supported(documentedWindow: "0.137.x") + versionString: "codex-cli 0.138.0", + compatibility: .supported(documentedWindow: "0.138.x") ) ) let client = CodexAppServer(transport: transport) @@ -124,7 +124,7 @@ struct CodexAppServerTests { ) ) - #expect(startup.cliExecutableDiagnostics.versionString == "codex-cli 0.137.0") + #expect(startup.cliExecutableDiagnostics.versionString == "codex-cli 0.138.0") #expect(startup.initializeSession.codexHome == "/Users/galew/.codex") #expect(await transport.recordedMethods == ["initialize", "initialized"]) @@ -140,7 +140,7 @@ struct CodexAppServerTests { resolvedExecutableURL: URL(fileURLWithPath: "/opt/homebrew/bin/codex"), source: .homebrewAppleSilicon, versionString: "codex-cli 0.128.0", - compatibility: .outsideDocumentedWindow(documentedWindow: "0.137.x") + compatibility: .outsideDocumentedWindow(documentedWindow: "0.138.x") ) ) let client = CodexAppServer(transport: transport) @@ -150,7 +150,7 @@ struct CodexAppServerTests { source: .homebrewAppleSilicon, resolvedExecutablePath: "/opt/homebrew/bin/codex", versionString: "codex-cli 0.128.0", - compatibility: .outsideDocumentedWindow(documentedWindow: "0.137.x") + compatibility: .outsideDocumentedWindow(documentedWindow: "0.138.x") ) )) { try await client.start( @@ -177,7 +177,7 @@ struct CodexAppServerTests { resolvedExecutableURL: URL(fileURLWithPath: "/opt/homebrew/bin/codex"), source: .homebrewAppleSilicon, versionString: "codex-cli 0.128.0", - compatibility: .outsideDocumentedWindow(documentedWindow: "0.137.x") + compatibility: .outsideDocumentedWindow(documentedWindow: "0.138.x") ) ) let client = CodexAppServer(transport: transport) diff --git a/Tests/SwiftASBTests/Transport/CodexCLIExecutableResolverTests.swift b/Tests/SwiftASBTests/Transport/CodexCLIExecutableResolverTests.swift index d07cdf8..a7c80f4 100644 --- a/Tests/SwiftASBTests/Transport/CodexCLIExecutableResolverTests.swift +++ b/Tests/SwiftASBTests/Transport/CodexCLIExecutableResolverTests.swift @@ -23,8 +23,8 @@ struct CodexCLIExecutableResolverTests { #expect(resolution.launchExecutableURL == explicitURL) #expect(resolution.launchArgumentsPrefix.isEmpty) #expect(resolution.resolvedExecutableURL == explicitURL) - #expect(resolution.versionString == "codex-cli 0.137.0") - #expect(resolution.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(resolution.versionString == "codex-cli 0.138.0") + #expect(resolution.compatibility == .supported(documentedWindow: "0.138.x")) #expect(recorder.recordedInvocations == [ .init(executablePath: explicitURL.path, arguments: ["--version"]) ]) @@ -48,7 +48,7 @@ struct CodexCLIExecutableResolverTests { #expect(resolution.launchExecutableURL.path == "/usr/bin/env") #expect(resolution.launchArgumentsPrefix == ["codex"]) #expect(resolution.resolvedExecutableURL == nil) - #expect(resolution.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(resolution.compatibility == .supported(documentedWindow: "0.138.x")) #expect(recorder.recordedInvocations == [ .init(executablePath: "/usr/bin/env", arguments: ["codex", "--version"]) ]) @@ -76,7 +76,7 @@ struct CodexCLIExecutableResolverTests { #expect(resolution.launchExecutableURL.path == homebrewPath) #expect(resolution.launchArgumentsPrefix.isEmpty) #expect(resolution.resolvedExecutableURL?.path == homebrewPath) - #expect(resolution.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(resolution.compatibility == .supported(documentedWindow: "0.138.x")) #expect(recorder.recordedInvocations == [ .init(executablePath: "/usr/bin/env", arguments: ["codex", "--version"]), .init(executablePath: homebrewPath, arguments: ["--version"]) @@ -106,7 +106,7 @@ struct CodexCLIExecutableResolverTests { #expect(resolution.launchExecutableURL.path == npmCodexPath) #expect(resolution.launchArgumentsPrefix.isEmpty) #expect(resolution.resolvedExecutableURL?.path == npmCodexPath) - #expect(resolution.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(resolution.compatibility == .supported(documentedWindow: "0.138.x")) #expect(recorder.recordedInvocations == [ .init(executablePath: "/usr/bin/env", arguments: ["codex", "--version"]), .init(executablePath: "/usr/bin/env", arguments: ["npm", "prefix", "-g"]), @@ -139,7 +139,7 @@ struct CodexCLIExecutableResolverTests { @Test("marks supported versions inside the documented support window") func marksSupportedVersionsInsideSupportWindow() throws { let explicitURL = URL(fileURLWithPath: "/tmp/codex-explicit") - let recorder = CommandRecorder(pathVersionStandardOutput: "codex-cli 0.137.3") + let recorder = CommandRecorder(pathVersionStandardOutput: "codex-cli 0.138.3") let resolver = CodexCLIExecutableResolver( explicitExecutableURL: explicitURL, @@ -150,7 +150,7 @@ struct CodexCLIExecutableResolverTests { ) let resolution = try resolver.resolve() - #expect(resolution.compatibility == .supported(documentedWindow: "0.137.x")) + #expect(resolution.compatibility == .supported(documentedWindow: "0.138.x")) } @Test("marks older minor versions outside the documented support window") @@ -167,7 +167,7 @@ struct CodexCLIExecutableResolverTests { ) let resolution = try resolver.resolve() - #expect(resolution.compatibility == .outsideDocumentedWindow(documentedWindow: "0.137.x")) + #expect(resolution.compatibility == .outsideDocumentedWindow(documentedWindow: "0.138.x")) } @Test("marks unparseable version strings as unknown format") @@ -184,7 +184,7 @@ struct CodexCLIExecutableResolverTests { ) let resolution = try resolver.resolve() - #expect(resolution.compatibility == .unknownVersionFormat(documentedWindow: "0.137.x")) + #expect(resolution.compatibility == .unknownVersionFormat(documentedWindow: "0.138.x")) } } @@ -204,7 +204,7 @@ private final class CommandRecorder: @unchecked Sendable { init( pathVersionTerminationStatus: Int32 = 0, - pathVersionStandardOutput: String = "codex-cli 0.137.0", + pathVersionStandardOutput: String = "codex-cli 0.138.0", pathVersionStandardError: String = "", npmPrefixTerminationStatus: Int32 = 0, npmPrefixOutput: String = "/Users/galew/.npm-global", diff --git a/Tools/AgentSB/tests/test_cli.py b/Tools/AgentSB/tests/test_cli.py index d78efb9..e26587a 100644 --- a/Tools/AgentSB/tests/test_cli.py +++ b/Tools/AgentSB/tests/test_cli.py @@ -19,7 +19,7 @@ def test_cli_inspect_outputs_json(repo_root, capsys): assert exit_code == 0 facts = json.loads(captured.out) - assert facts["reviewed_codex_cli_window"]["window"] == "0.137.x" + assert facts["reviewed_codex_cli_window"]["window"] == "0.138.x" def test_cli_schema_review_writes_report(fake_repo, capsys): diff --git a/Tools/AgentSB/tests/test_tools.py b/Tools/AgentSB/tests/test_tools.py index e2b467a..201f023 100644 --- a/Tools/AgentSB/tests/test_tools.py +++ b/Tools/AgentSB/tests/test_tools.py @@ -6,7 +6,7 @@ def test_inspect_repo_reads_swiftasb_facts(repo_root): facts = inspect_repo(repo_root) - assert facts["reviewed_codex_cli_window"]["window"] == "0.137.x" + assert facts["reviewed_codex_cli_window"]["window"] == "0.138.x" assert any( item["name"] == "CodexLifecycleV2Batch+JSONValue.swift" for item in facts["promoted_wire_files"] diff --git a/docs/agents/reports/2026-06-09-agentsb-maintenance-draft.md b/docs/agents/reports/2026-06-09-agentsb-maintenance-draft.md new file mode 100644 index 0000000..dc234ae --- /dev/null +++ b/docs/agents/reports/2026-06-09-agentsb-maintenance-draft.md @@ -0,0 +1,154 @@ +# AgentSB Maintenance Draft + +## Summary + +- Mode: `draft`. +- Git branch at inspection time: `maintenance/codex-schema-refresh`. +- Git dirty state at inspection time: `True`. +- Candidates reviewed: 5. +- Safe changes applied: 0. + +## Schema Diff Evidence + +- Compared `v0.137.0` to `v0.138.0`. +- Added JSON files: 4. +- Removed JSON files: 0. +- Changed JSON files: 35. +- Unchanged JSON files: 277. +- Added: `v2/GetAccountTokenUsageResponse.json`, `v2/RemoteControlPairingStatusParams.json`, `v2/RemoteControlPairingStatusResponse.json`, `v2/TurnModerationMetadataNotification.json`. +- Changed: `ClientRequest.json`, `ServerNotification.json`, `codex_app_server_protocol.schemas.json`, `codex_app_server_protocol.v2.schemas.json`, `v2/AccountUpdatedNotification.json`, `v2/CollaborationModeListResponse.json`, `v2/ConfigReadResponse.json`, `v2/ConfigRequirementsReadResponse.json`, ... 27 more. + +## Candidate Decisions + +### 1. Write schema-review evidence report + +- Decision: `auto-apply`. +- Change kind: `report-create`. +- Paths: `docs/agents/reports/2026-06-09-agentsb-schema-review-2.md`. +- Summary: Create an AgentSB-owned schema-review report with deterministic repo facts and schema diff evidence. +- Reasons: `candidate is limited to AgentSB-owned report formatting or report creation`. +- Required checks: `uv run pytest`. + +### 2. Draft Codex CLI compatibility alignment patch + +- Decision: `draft-only`. +- Change kind: `compatibility-alignment`. +- Paths: `README.md`, `ROADMAP.md`, `docs/maintainers/interactive-lifecycle-release-boundary.md`, `scripts/generate-wire-types.sh`, `Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift`, `Tools/AgentSB/tests/test_cli.py`, `Tools/AgentSB/tests/test_tools.py`. +- Summary: Draft the predictable version-window updates needed after maintainers classify `v0.138.0` as the reviewed Codex CLI schema. +- Reasons: `Codex CLI compatibility alignment changes need maintainer review before application`. +- Required checks: `swift build`, `swift test`, `uv run pytest`, `git diff --check`. + +Proposed patch: + +```diff +diff --git a/scripts/generate-wire-types.sh b/scripts/generate-wire-types.sh +--- a/scripts/generate-wire-types.sh ++++ b/scripts/generate-wire-types.sh +@@ +-SCHEMA_VERSION=${SCHEMA_VERSION:-v0.137.0} ++SCHEMA_VERSION=${SCHEMA_VERSION:-v0.138.0} +diff --git a/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift b/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift +--- a/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift ++++ b/Sources/SwiftASB/Transport/CodexCLIExecutableResolver.swift +@@ +- internal static let latestSupportedPublicRelease = Version(major: 0, minor: , patch: 0) ++ internal static let latestSupportedPublicRelease = Version(major: 0, minor: 138, patch: 0) +diff --git a/README.md b/README.md +--- a/README.md ++++ b/README.md +@@ +-*Note: SwiftASB currently supports the latest reviewed Codex CLI minor release, `0.137.x`.* ++*Note: SwiftASB currently supports the latest reviewed Codex CLI minor release, `0.138.x`.* +diff --git a/ROADMAP.md b/ROADMAP.md +--- a/ROADMAP.md ++++ b/ROADMAP.md +@@ +-The current reviewed compatibility window is `codex-cli 0.137.x` ++The current reviewed compatibility window is `codex-cli 0.138.x` +@@ ++- [ ] Classify the Codex CLI `v0.138.0` schema diff before promotion. ++ Decision: update the reviewed CLI window to `0.138.x` only after ++ generated-wire and public API boundary review is complete. +diff --git a/docs/maintainers/interactive-lifecycle-release-boundary.md b/docs/maintainers/interactive-lifecycle-release-boundary.md +--- a/docs/maintainers/interactive-lifecycle-release-boundary.md ++++ b/docs/maintainers/interactive-lifecycle-release-boundary.md +@@ +-- current reviewed minor release: `0.137.x` ++- current reviewed minor release: `0.138.x` +diff --git a/Tools/AgentSB/tests/test_cli.py b/Tools/AgentSB/tests/test_cli.py +--- a/Tools/AgentSB/tests/test_cli.py ++++ b/Tools/AgentSB/tests/test_cli.py +@@ +- assert facts["reviewed_codex_cli_window"]["window"] == "0.137.x" ++ assert facts["reviewed_codex_cli_window"]["window"] == "0.138.x" +diff --git a/Tools/AgentSB/tests/test_tools.py b/Tools/AgentSB/tests/test_tools.py +--- a/Tools/AgentSB/tests/test_tools.py ++++ b/Tools/AgentSB/tests/test_tools.py +@@ +- assert facts["reviewed_codex_cli_window"]["window"] == "0.137.x" ++ assert facts["reviewed_codex_cli_window"]["window"] == "0.138.x" +``` + +### 3. Draft generated-wire schema membership patch + +- Decision: `draft-only`. +- Change kind: `schema-generator-membership`. +- Paths: `scripts/generate-wire-types.sh`. +- Summary: Draft the generator-script additions for new schema files so maintainers can promote the classified wire batch through the normal script. +- Reasons: `Codex CLI compatibility alignment changes need maintainer review before application`. +- Required checks: `swift build`, `swift test`, `uv run pytest`, `git diff --check`. + +Proposed patch: + +```diff +diff --git a/scripts/generate-wire-types.sh b/scripts/generate-wire-types.sh +--- a/scripts/generate-wire-types.sh ++++ b/scripts/generate-wire-types.sh +@@ + # Add classified schema families to the consolidated v2 batch. ++ GetAccountTokenUsageResponse \ ++ RemoteControlPairingStatusParams \ ++ RemoteControlPairingStatusResponse \ ++ TurnModerationMetadataNotification \ +``` + +### 4. Classify schema family changes before generated-wire promotion + +- Decision: `report-only`. +- Change kind: `schema-family-promotion`. +- Paths: `Sources/SwiftASB/Generated/CodexWire/Latest/CodexLifecycleV2Batch+JSONValue.swift`. +- Summary: Schema dumps `v0.137.0` and `v0.138.0` differ; maintainers need to classify added or changed families before promotion. +- Reasons: `generated wire snapshots require maintainer-controlled promotion`. + +### 5. Draft AgentSB roadmap evidence note + +- Decision: `draft-only`. +- Change kind: `docs-update`. +- Paths: `docs/agents/agentsb-roadmap.md`. +- Summary: Propose a roadmap note that records the latest local schema diff evidence without changing the source document. +- Reasons: `candidate has unresolved ambiguity and needs maintainer review before application`. +- Required checks: `review drafted diff`. + +Proposed patch: + +```diff +diff --git a/docs/agents/agentsb-roadmap.md b/docs/agents/agentsb-roadmap.md +--- a/docs/agents/agentsb-roadmap.md ++++ b/docs/agents/agentsb-roadmap.md +@@ ++- Latest local schema diff evidence: AgentSB compared `v0.137.0` to `v0.138.0` and found 4 added, 0 removed, and 35 changed JSON files. Do not update public support claims or generated wire output until maintainers classify each changed family. +``` + +## Applied Changes + +No safe changes were applied. + +## Required Checks + +No checks were run. + +## Evidence + +- Repository root: `/Users/galew/Workspace/gaelic-ghost/SwiftASB`. +- Reviewed window source: `ROADMAP.md`. +- Git upstream: `none`. diff --git a/docs/agents/reports/2026-06-09-agentsb-schema-review.md b/docs/agents/reports/2026-06-09-agentsb-schema-review.md new file mode 100644 index 0000000..2e1cafe --- /dev/null +++ b/docs/agents/reports/2026-06-09-agentsb-schema-review.md @@ -0,0 +1,69 @@ +# AgentSB Schema Review + +## Summary + +- Reviewed Codex CLI compatibility window: `0.137.x`. +- Latest discovered schema dump: `v0.138.0`. +- Promoted generated wire files: 2. +- Git branch at inspection time: `maintenance/codex-schema-refresh`. + +## Codex CLI Schema State + +| Dump | Variant | JSON files | +| --- | --- | --- | +| `v0.124.0` | experimental | 225 | +| `v0.125.0` | experimental | 227 | +| `v0.128.0` | experimental | 269 | +| `v0.129.0` | experimental | 290 | +| `v0.130.0` | experimental | 286 | +| `v0.132.0` | experimental | 297 | +| `v0.133.0` | experimental | 302 | +| `v0.135.0` | experimental | 304 | +| `v0.137.0` | experimental | 312 | +| `v0.138.0` | experimental | 316 | + +## Schema Diff Evidence + +- Compared `v0.137.0` to `v0.138.0`. +- Added JSON files: 4. +- Removed JSON files: 0. +- Changed JSON files: 35. +- Unchanged JSON files: 277. +- Added: `v2/GetAccountTokenUsageResponse.json`, `v2/RemoteControlPairingStatusParams.json`, `v2/RemoteControlPairingStatusResponse.json`, `v2/TurnModerationMetadataNotification.json`. +- Changed: `ClientRequest.json`, `ServerNotification.json`, `codex_app_server_protocol.schemas.json`, `codex_app_server_protocol.v2.schemas.json`, `v2/AccountUpdatedNotification.json`, `v2/CollaborationModeListResponse.json`, `v2/ConfigReadResponse.json`, `v2/ConfigRequirementsReadResponse.json`, ... 27 more. + +## Boundary Review + +- Report skeleton only: classify any new schema families as `public now`, `observable-only`, or `internal-only` before promotion. +- Do not expose generated `CodexWire...` models as public Swift API without a hand-owned SwiftASB boundary. + +## Documentation Drift + +| Document | Present | Bytes | +| --- | --- | --- | +| `AGENTS.md` | true | 17089 | +| `README.md` | true | 8375 | +| `CONTRIBUTING.md` | true | 8580 | +| `ROADMAP.md` | true | 127499 | +| `docs/maintainers/*.md` | true | 13 files | + +## Recommended Probes + +- Run `swift build` and `swift test` after package behavior changes. +- Run `scripts/run-live-codex-integration-tests.sh smoke` for runtime confidence after schema-boundary changes. +- Run `xcodebuild docbuild -scheme SwiftASB -destination generic/platform=macOS -derivedDataPath tmp/xcode-docc/DerivedData` after DocC changes. + +## Human Decisions + +- Decide whether any newly dumped schema family deserves public API, observable-only support, or internal-only coverage. +- Decide whether README, CONTRIBUTING, ROADMAP, or DocC need compatibility-window updates. + +## Evidence + +- Repository root: `/Users/galew/Workspace/gaelic-ghost/SwiftASB`. +- Git dirty state: `False`. +- Git upstream: `none`. +- Reviewed window source: `ROADMAP.md`. +- Promoted wire files: + - `Sources/SwiftASB/Generated/CodexWire/Latest/CodexLifecycleV2Batch+JSONValue.swift` (229429 bytes) + - `Sources/SwiftASB/Generated/CodexWire/Latest/CodexWireInitializeResponse.swift` (841 bytes) diff --git a/docs/maintainers/interactive-lifecycle-release-boundary.md b/docs/maintainers/interactive-lifecycle-release-boundary.md index ec640cc..38a25fb 100644 --- a/docs/maintainers/interactive-lifecycle-release-boundary.md +++ b/docs/maintainers/interactive-lifecycle-release-boundary.md @@ -50,7 +50,7 @@ runtime while the app-server schema is moving quickly before v1. Current policy: - support the latest reviewed public Codex CLI minor release -- current reviewed minor release: `0.137.x` +- current reviewed minor release: `0.138.x` - widen back to a rolling window only after the latest generated-wire and public API boundaries have caught up with the current app-server shape - reassess this policy when Codex reaches a future major-version release diff --git a/scripts/generate-wire-types.sh b/scripts/generate-wire-types.sh index f2eba72..24fd2ec 100755 --- a/scripts/generate-wire-types.sh +++ b/scripts/generate-wire-types.sh @@ -2,7 +2,7 @@ set -eu ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -SCHEMA_VERSION=${SCHEMA_VERSION:-v0.137.0} +SCHEMA_VERSION=${SCHEMA_VERSION:-v0.138.0} SCHEMA_ROOT="$ROOT_DIR/codex-schemas/$SCHEMA_VERSION" DERIVED_DIR="$ROOT_DIR/tmp/derived-schemas/${SCHEMA_VERSION//./_}" OUT_DIR="$ROOT_DIR/tmp/quicktype-wire/${SCHEMA_VERSION//./_}" @@ -142,6 +142,7 @@ build_batch \ ConfigReadParams \ ConfigReadResponse \ ConfigRequirementsReadResponse \ + GetAccountTokenUsageResponse \ ConfigWarningNotification \ AppsListParams \ AppsListResponse \ @@ -178,6 +179,7 @@ build_batch \ TurnStartParams \ TurnStartResponse \ TurnStartedNotification \ + TurnModerationMetadataNotification \ TurnPlanUpdatedNotification \ TurnDiffUpdatedNotification \ TurnCompletedNotification \ @@ -217,6 +219,8 @@ build_batch \ RemoteControlStatusChangedNotification \ RemoteControlPairingStartParams \ RemoteControlPairingStartResponse \ + RemoteControlPairingStatusParams \ + RemoteControlPairingStatusResponse \ RemoteControlClientsListParams \ RemoteControlClientsListResponse \ RemoteControlClientsRevokeParams \ From 86d42fa7777404cad05a59486632a916af5413a4 Mon Sep 17 00:00:00 2001 From: Gale W Date: Tue, 9 Jun 2026 21:37:22 -0400 Subject: [PATCH 3/5] release: prep v1.7.2 license shift --- COMMERCIAL-USE.md | 40 +++ CONTRIBUTING.md | 6 +- LICENSE | 318 +++++++++--------------- LICENSE-APACHE-2.0 | 201 +++++++++++++++ NOTICE | 37 +++ README.md | 12 +- ROADMAP.md | 54 ++-- Tools/AgentSB/pyproject.toml | 2 +- docs/maintainers/v1-public-api-audit.md | 2 +- docs/releases/v1.7.2.md | 30 +++ 10 files changed, 475 insertions(+), 227 deletions(-) create mode 100644 COMMERCIAL-USE.md create mode 100644 LICENSE-APACHE-2.0 create mode 100644 NOTICE create mode 100644 docs/releases/v1.7.2.md diff --git a/COMMERCIAL-USE.md b/COMMERCIAL-USE.md new file mode 100644 index 0000000..dcb5ab2 --- /dev/null +++ b/COMMERCIAL-USE.md @@ -0,0 +1,40 @@ +# Commercial Use + +SwiftASB is licensed under the PolyForm Noncommercial License 1.0.0 for +noncommercial use. The full license text is in [LICENSE](./LICENSE), and the +required notice is in [NOTICE](./NOTICE). + +## Noncommercial Use + +You may use SwiftASB under the PolyForm Noncommercial License for personal, +hobby, educational, research, public-interest, and other noncommercial work +that is not intended to generate revenue, reduce business costs, or provide a +commercial advantage. + +## Commercial Use + +Commercial use requires a separate written commercial license from Gale. + +For commercial licensing, contact Gale W at . + +Commercial use includes, without limitation, using SwiftASB, its documentation, +examples, generated wire models, maintainer tools, AgentSB, UI component +targets, package artifacts, or derived works: + +- for a business, employer, client, customer, or contractor relationship +- in a paid product, paid service, hosted service, or internal business tool +- for training, consulting, professional services, or product development +- to develop, evaluate, operate, or improve a commercial agentic coding + application or workflow +- to generate revenue, reduce business costs, or provide commercial advantage + +## Earlier Versions + +SwiftASB versions published before the PolyForm Noncommercial change remain +available under the license terms that applied to those versions. The +historical Apache License 2.0 text is preserved in +[LICENSE-APACHE-2.0](./LICENSE-APACHE-2.0). + +## Required Notice + +Required Notice: Copyright 2026 Gale Williams (mailto:mail@galewilliams.com) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ee568a..b8fa6be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -172,4 +172,8 @@ Record settled decisions in `ROADMAP.md`, DocC, maintainer docs, or this guide s ## License and Contribution Terms -SwiftASB is licensed under the Apache License, Version 2.0. Contributions are accepted under the same license terms unless maintainers state a different written agreement before the work lands. +Unless a contribution explicitly says otherwise in writing, contributions to SwiftASB are made under the PolyForm Noncommercial License 1.0.0 terms in [LICENSE](./LICENSE). The root legal-notice surface lives in [NOTICE](./NOTICE). + +Commercial use requires a separate written commercial license from Gale. For commercial licensing, contact Gale W at . See [COMMERCIAL-USE.md](./COMMERCIAL-USE.md) for the current commercial-use policy. + +Do not submit contributions unless you have the right to license them under these terms and understand Gale may offer separate commercial licenses for SwiftASB. diff --git a/LICENSE b/LICENSE index 261eeb9..0da8bbe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,117 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +# PolyForm Noncommercial License 1.0.0 + +## Acceptance + +In order to get any license under these terms, you must agree to them as +both strict obligations and conditions to all your licenses. + +## Copyright License + +The licensor grants you a copyright license for the software to do +everything you might do with the software that would otherwise infringe +the licensor's copyright in it for any permitted purpose. However, you +may only distribute the software according to [Distribution License](#distribution-license) +and make changes or new works based on the software according to +[Changes and New Works License](#changes-and-new-works-license). + +## Distribution License + +The licensor grants you an additional copyright license to distribute +copies of the software. Your license to distribute covers distributing +the software with changes and new works permitted by [Changes and New +Works License](#changes-and-new-works-license). + +## Notices + +You must ensure that anyone who gets a copy of any part of the software +from you also gets a copy of these terms or the URL for them above, as +well as copies of any plain-text lines beginning with `Required Notice:` +that the licensor provided with the software. For example: + +> Required Notice: Copyright Yoyodyne, Inc. (http://example.com) + +## Changes and New Works License + +The licensor grants you an additional copyright license to make changes +and new works based on the software for any permitted purpose. + +## Patent License + +The licensor grants you a patent license for the software that covers +patent claims the licensor can license, or becomes able to license, that +you would infringe by using the software. + +## Noncommercial Purposes + +Any noncommercial purpose is a permitted purpose. + +## Personal Uses + +Personal use for research, experiment, and testing for the benefit of +public knowledge, personal study, private entertainment, hobby projects, +amateur pursuits, or religious observance, without any anticipated +commercial application, is use for a permitted purpose. + +## Noncommercial Organizations + +Use by any charitable organization, educational institution, public +research organization, public safety or health organization, +environmental protection organization, or government institution is use +for a permitted purpose regardless of the source of funding or +obligations resulting from the funding. + +## Fair Use + +You may have "fair use" rights for the software under the law. These +terms do not limit them. + +## No Other Rights + +These terms do not allow you to sublicense or transfer any of your +licenses to anyone else, or prevent the licensor from granting licenses +to anyone else. These terms do not imply any other licenses. + +## Patent Defense + +If you make any written claim that the software infringes or contributes +to infringement of any patent, your patent license for the software +granted under these terms ends immediately. If your company makes such a +claim, your patent license ends immediately for work on behalf of your +company. + +## Violations + +The first time you are notified in writing that you have violated any of +these terms, or done anything with the software not covered by your +licenses, your licenses can nonetheless continue if you come into full +compliance with these terms, and take practical steps to correct past +violations, within 32 days of receiving notice. Otherwise, all your +licenses end immediately. + +## No Liability + +As far as the law allows, the software comes as is, without any warranty +or condition, and the licensor will not be liable to you for any damages +arising out of these terms or the use or nature of the software, under +any kind of legal claim. + +## Definitions + +The **licensor** is the individual or entity offering these terms, and +the **software** is the software the licensor makes available under these +terms. + +**You** refers to the individual or entity agreeing to these terms. + +**Your company** is any legal entity, sole proprietorship, or other kind +of organization that you work for, plus all organizations that have +control over, are under the control of, or are under common control with +that organization. **Control** means ownership of substantially all the +assets of an entity, or the power to direct its management and policies +by vote, contract, or otherwise. Control can be direct or indirect. + +**Your licenses** are all the licenses granted to you for the software +under these terms. + +**Use** means anything you do with the software requiring one of your +licenses. diff --git a/LICENSE-APACHE-2.0 b/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..db9f883 --- /dev/null +++ b/NOTICE @@ -0,0 +1,37 @@ +SwiftASB +Copyright 2026 Gale Williams + +Required Notice: Copyright 2026 Gale Williams (mailto:mail@galewilliams.com) + +This repository is licensed under the PolyForm Noncommercial License 1.0.0. +See [LICENSE](./LICENSE) for the full license text. + +Commercial use requires a separate written commercial license from Gale. +For commercial licensing, contact Gale W at . +See [COMMERCIAL-USE.md](./COMMERCIAL-USE.md) for the current commercial-use +policy. + +SwiftASB versions published before the PolyForm Noncommercial change remain +available under the license terms that applied to those versions. The historical +Apache License 2.0 text is preserved in [LICENSE-APACHE-2.0](./LICENSE-APACHE-2.0). + +Third-Party Trademarks And Brand References + +SwiftASB refers to third-party technologies, developer tools, programming +languages, product names, and visual identities for identification, +compatibility, and workflow description only. Those references do not imply +sponsorship, endorsement, approval, or affiliation unless an explicit written +license or partnership statement says so. + +SwiftASB and Gaelic Ghost are independent projects and are not affiliated with, +authorized by, sponsored by, endorsed by, or otherwise approved by Apple Inc., +the Swift open source project, OpenAI, or the Codex project. + +Apple, Xcode, Swift, SwiftUI, SwiftData, Safari, TestFlight, and the Swift Logo +are trademarks of Apple Inc., registered in the U.S. and other countries and +regions. OpenAI, ChatGPT, and Codex are trademarks or service marks of OpenAI, +L.L.C. or its affiliates. Any references in this repository are referential +and descriptive. + +All other third-party trademarks, service marks, names, logos, and brand +features are the property of their respective owners. diff --git a/README.md b/README.md index 4d041db..87d2548 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Listen to the SwiftASB Codex apps promo clip: ### Status -SwiftASB is actively maintained and supported by Gale. Our current API is v1, and `v1.7.1` is the current and latest release. +SwiftASB is actively maintained and supported by Gale. Our current API is v1, and `v1.7.2` is the current and latest release. ### What This Project Is @@ -38,7 +38,7 @@ I built SwiftASB because I saw so many others building and forking existing Apps Add SwiftASB to your `Package.swift` dependencies: ```swift -.package(url: "https://github.com/gaelic-ghost/SwiftASB", from: "1.7.1"), +.package(url: "https://github.com/gaelic-ghost/SwiftASB", from: "1.7.2"), ``` Then add the library product to your target dependencies: @@ -137,8 +137,12 @@ Agent-facing maintainer guidance lives in [AGENTS.md](./AGENTS.md). ## Release Notes -`ROADMAP.md` tracks milestone status and release-facing work. Git tags and GitHub releases are the source of truth for published versions. +`ROADMAP.md` tracks milestone status and release-facing work. `docs/releases/v1.7.2.md` contains the current patch release note draft. Git tags and GitHub releases are the source of truth for published versions. ## License -SwiftASB is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE). +SwiftASB is licensed under the PolyForm Noncommercial License 1.0.0 for future public versions. See [LICENSE](./LICENSE), [NOTICE](./NOTICE), and [COMMERCIAL-USE.md](./COMMERCIAL-USE.md). + +Commercial use requires a separate written commercial license from Gale. For commercial licensing, contact Gale W at . + +SwiftASB versions published before the PolyForm Noncommercial change remain available under the license terms that applied to those versions. The historical Apache License 2.0 text is preserved in [LICENSE-APACHE-2.0](./LICENSE-APACHE-2.0). diff --git a/ROADMAP.md b/ROADMAP.md index 326a1cd..94efbdf 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -80,7 +80,7 @@ | Non-UI local history-reading helpers | `Partially shipped` | `CodexThread` now exposes a lightweight `HistoryWindow` page shape for recent local history, older or newer local windows around a known boundary turn id, centered `windowAroundTurn(...)` reads, centered `windowAroundItem(...)` reads, direct `ClosedTurn` reads for one turn, and convenience array helpers over those same windows. This gives non-UI callers an intentional path into the local history store without binding a UI-oriented observable, while still deferring a broader public cursor model, transcript search surface, and richer history-query helpers. | | Public API curation | `Shipped / ongoing` | The source-organization pass has split app-wide model, MCP, thread-management, history, and observable companion values into focused public files while preserving `CodexAppServer`, `CodexThread`, and `CodexTurnHandle` as the three real owners. The connected public-surface review closed the v1 ownership model; post-v1 curation now includes app-server-owned project identity and thread source facts for launcher UI without exposing generated wire models. Future curation should stay tied to concrete public API additions. | | DocC documentation | `Shipped / ongoing` | `Sources/SwiftASB/SwiftASB.docc/` contains a package landing page, public-handle extension pages, conceptual articles for app-wide capabilities, interactive lifecycle, thread management, history/observable companions, generated-wire boundary notes, and copy-pasteable walkthroughs for startup, progress/approval handling, diagnostics/history, and SwiftUI observable companions. The catalog is validated through Xcode `docbuild`; future work is ordinary stale-link, prose, and symbol-comment refinement as the public API grows. | -| Swift Package Index readiness | `Shipped` | `.spi.yml` declares `SwiftASB` as the documentation target, and Swift Package Index lists `gaelic-ghost/SwiftASB` with a documentation link, compatibility/build results, Package ID `9B5839D9-9551-473F-A939-841534A3FC55`, and a 2026-05-06 update timestamp for the latest confirmed indexed release. Recheck SPI after the `v1.7.1` tag is published. | +| Swift Package Index readiness | `Shipped` | `.spi.yml` declares `SwiftASB` as the documentation target, and Swift Package Index lists `gaelic-ghost/SwiftASB` with a documentation link, compatibility/build results, Package ID `9B5839D9-9551-473F-A939-841534A3FC55`, and a 2026-05-06 update timestamp for the latest confirmed indexed release. Recheck SPI after the `v1.7.2` tag is published. | | Contributor documentation split | `Shipped` | `README.md` is now focused on Swift and SwiftUI package users, while `CONTRIBUTING.md` owns contributor setup, validation, DocC, live-test flags, generated-wire refresh, and PR expectations. | | `CodexTurnHandle` live observable companion | `Partially shipped` | `CodexTurnHandle` owns a live `Minimap` companion that is attached when the handle is created and maintains current-state call snapshots for command, file-edit, dynamic-tool, collab-tool, and MCP item activity. It also now mirrors whether thread context compaction is active for the turn and supports explicit `complete()` handoff into a caller-owned sealed turn snapshot. | | Additional turn event mapping | `Partially shipped` | The public event layer covers the current interactive lifecycle plus the item-start and item-complete events needed for observable call-state mirrors. Raw command-output and file-change-output deltas now stay internal as transport detail but drive the shipped `RecentCommands` and `RecentFiles` companions, and streamed or patch-updated payloads are preserved when later completed snapshots are thinner. Richer MCP-progress detail still remains internal, while warning, guardian-warning, config-warning, deprecation, MCP-server-status, remote-control-status, model-reroute, and model-verification notifications now surface through hand-owned diagnostic events. | @@ -93,7 +93,7 @@ | AgentSB maintainer automation | `Report-first maintainer app` | `Tools/AgentSB/` is a repo-local Python maintainer app that inspects SwiftASB deterministically, writes tracked reports under `docs/agents/reports/`, evaluates safety-boundary cases, diffs schema dumps, writes reviewable maintenance drafts, and prototypes local Codex thread-index inspection for future SwiftASB planning. The v1 boundary stays report-first: safe auto-apply is classifier-gated and limited to AgentSB-owned report artifacts, and it must not mutate Swift source, generated wire snapshots, public API, releases, or behavior-changing docs. | | Agent workflow guidance | `Shipped / ongoing` | SwiftASB-specific Codex guidance now ships through `socket`'s [`swiftasb-skills`](https://github.com/gaelic-ghost/socket/tree/main/plugins/swiftasb-skills) plugin, with skills for explaining SwiftASB, choosing an integration shape, building SwiftUI-facing app state, and diagnosing integration failures. This repo now points package users and maintainers at that plugin while keeping SwiftASB source, DocC, tests, generated-wire review, and release notes here as the package source of truth. | | End-to-end subprocess integration tests | `Shipped / ongoing` | The package includes opt-in live Codex CLI integration tests with temp workspaces and time limits, including raw transport startup, single-turn completion, cross-thread completion, app-wide model/MCP/hook diagnostics snapshots, thread-name mutation, stored-history materialization, same-thread concurrency probing, deterministic command and permissions approvals through a mock Responses provider, a best-effort prompt-driven approval-path probe, a disposable live rollback scenario, and a multi-turn file-mutation scenario that creates, edits, and deletes files through the real CLI. The umbrella runner is `scripts/run-live-codex-integration-tests.sh`; it defaults to the release-gate set and exposes focused modes for smoke, transport, capability, thread, turn, approval, file-scenario, rollback, same-thread, and all opt-in live tests. Stored-history materialization remains in focused `thread`/`all` runs instead of the release-gate smoke group because the live app-server can delay history materialization. | -| Apache 2.0 licensing | `Shipped` | The repo now carries the Apache License, Version 2.0 text, and README plus contributor docs describe current releases as Apache 2.0 licensed. | +| PolyForm Noncommercial licensing | `Release-prepped` | Future public versions now use the PolyForm Noncommercial License 1.0.0 with a separate commercial-use policy. The historical Apache License 2.0 text is preserved for versions published before the license change. | ## Milestone Progress @@ -112,7 +112,7 @@ lifecycle, SPI visibility, basic history hydration, first-pass reconciliation, or command-approval completion. Those slices now exist and shipped in the `v1.7.1` baseline. -The next meaningful work is to probe and deliberately shape the newly promoted +The next meaningful work after the `v1.7.2` patch release is to probe and deliberately shape the newly promoted Codex CLI `0.138.x` wire families before widening public API. Remote-control pairing/client management and pairing status, account token usage, turn moderation metadata, plugin app templates, skills extra roots, richer @@ -279,11 +279,27 @@ consuming apps to adopt: ## Current Patch Release Slice -This slice records the `v1.7.1` and follow-up compatibility patches. It does -not widen the public API boundary. Its job is to keep SwiftASB aligned with the -latest reviewed Codex CLI `0.138.x` app-server schema while preserving generated -wire families as internal scaffolding until their behavior and ownership model -are probed. +This slice records the `v1.7.2` patch release prep. It does not widen the +public SwiftASB API boundary. Its job is to ship the ASBPresentation and +ASBAppKit foundation, keep SwiftASB aligned with the latest reviewed Codex CLI +`0.138.x` app-server schema, and move future public versions to PolyForm +Noncommercial licensing while preserving historical terms for earlier versions. + +### Planned for v1.7.2 + +- [x] Land `ASBPresentation` as the shared renderer-neutral UI model target for + snapshots, intents, and cache policy values. +- [x] Land the first `ASBAppKit` thread sidebar view backed by + `ThreadSidebarSnapshot` data. +- [x] Classify the Codex CLI `v0.138.0` schema diff before promotion. + Decision: refresh the promoted v2 lifecycle batch, update the reviewed CLI + window to `0.138.x`, promote account token usage, remote-control pairing + status, and turn moderation metadata wire types internally, and keep those + schema families out of public API until their user-facing jobs and ownership + model are deliberately designed. +- [x] Move future public versions to the PolyForm Noncommercial License 1.0.0, + preserve historical Apache License 2.0 text, and add a commercial-use policy. +- [ ] Run the final release workflow for `v1.7.2`. ### Shipped in v1.7.1 @@ -301,13 +317,6 @@ are probed. forking probe-first instead of promising stable public request parameters in this patch. - [x] Exercise deterministic Swift and AgentSB tests before tagging the patch. -- [x] Classify the Codex CLI `v0.138.0` schema diff before promotion. - Decision: refresh the promoted v2 lifecycle batch, update the reviewed CLI - window to `0.138.x`, promote account token usage, remote-control pairing - status, and turn moderation metadata wire types internally, and keep those - schema families out of public API until their user-facing jobs and ownership - model are deliberately designed. - ### Follow-Up Probes - [ ] Investigate the Codex GUI remote-control pairing/auth setup and decide @@ -568,8 +577,9 @@ workflow earns them in a later feature release. ### Documentation And Examples - [x] Update stale release references after the `v1.7.1` release. - Decision: README now names `v1.7.1` as the current released baseline and no - longer describes the package as early development. + Decision: README named `v1.7.1` as the current released baseline and no + longer described the package as early development. The `v1.7.2` patch prep + updates the current release reference again. - [x] Finish DocC symbol comments for the supported lifecycle, not just the conceptual articles. Decision: the source-level documentation pass now covers @@ -838,7 +848,7 @@ workflow earns them in a later feature release. #### Migration Notes - Existing `v0.9.x` consumers should update the SwiftPM dependency to - `from: "1.7.1"` once the tag is published. + `from: "1.7.2"` once the tag is published. - The v1 API surface has removed stale pre-v1 compatibility shims and phantom fields that no longer exist in the reviewed `v0.128.0` schema. - Same-thread overlapping turns are rejected client-side with @@ -863,7 +873,7 @@ workflow earns them in a later feature release. - Keep an eye on future Swift Package Index builds after compatibility-window or DocC changes; the `v1.1.1` listing and documentation link are live, and - `v1.7.1` should be rechecked after the patch tag is indexed. + `v1.7.2` should be rechecked after the patch tag is indexed. - Add broader live server-request coverage for permissions and MCP elicitation if those become stronger public runtime guarantees. - Continue tuning recent companion cache calibration, richer file previews, @@ -1500,6 +1510,9 @@ Completed - [x] Decide whether real subprocess integration tests are required before the first release. Decision: yes, but as opt-in suites rather than as part of the default `swift test` path while the live Codex runtime remains an external local dependency. - [x] Add an explicit source-available license for the package. + Decision: future public versions now use the PolyForm Noncommercial License + 1.0.0, commercial use requires a separate written license from Gale, and the + historical Apache License 2.0 text is preserved for earlier versions. ### Exit Criteria @@ -1617,6 +1630,9 @@ Completed ## History +- 2026-06-09: Prepared the `v1.7.2` release branch with the ASBPresentation + foundation, first ASBAppKit thread sidebar view, Codex CLI `0.138.x` + generated-wire refresh, and PolyForm Noncommercial license transition. - 2026-06-06: Used the AgentSB schema-review and auto-apply-safe reports as the basis for the Codex CLI `0.137.x` compatibility refresh, promoted the v0.137.0 generated wire snapshot internally, and kept new remote-control and diff --git a/Tools/AgentSB/pyproject.toml b/Tools/AgentSB/pyproject.toml index c6416a9..cbe9280 100644 --- a/Tools/AgentSB/pyproject.toml +++ b/Tools/AgentSB/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Repo-local maintenance agents for SwiftASB." readme = "README.md" requires-python = ">=3.11" -license = "Apache-2.0" +license = "PolyForm-Noncommercial-1.0.0" authors = [ { name = "Gale W", email = "mail@galewilliams.com" }, ] diff --git a/docs/maintainers/v1-public-api-audit.md b/docs/maintainers/v1-public-api-audit.md index 6dfcd0e..bcef4cf 100644 --- a/docs/maintainers/v1-public-api-audit.md +++ b/docs/maintainers/v1-public-api-audit.md @@ -514,7 +514,7 @@ Use these decisions for every public symbol: Decision: covered by the startup, progress/approval, diagnostics/history, and SwiftUI observable companion walkthroughs in `Sources/SwiftASB/SwiftASB.docc/`. - [x] Update stale README release references before the next release. - Decision: README now names `v1.7.1` as the current released baseline. + Decision: README now names `v1.7.2` as the current released baseline. - [x] Confirm README, DocC, and this audit use the same v1 release boundary. Decision: README, DocC, and this audit now describe the same narrow v1 promise: app-server lifecycle, app-wide capability reads, stored-thread diff --git a/docs/releases/v1.7.2.md b/docs/releases/v1.7.2.md new file mode 100644 index 0000000..c261695 --- /dev/null +++ b/docs/releases/v1.7.2.md @@ -0,0 +1,30 @@ +# SwiftASB v1.7.2 Release Notes + +## What Changed + +- Added the `ASBPresentation` foundation for renderer-neutral UI snapshots, + intents, and cache policy values. +- Added the first `ASBAppKit` thread sidebar view backed by + `ThreadSidebarSnapshot` data. +- Refreshed the internal Codex wire snapshot and reviewed compatibility window + for Codex CLI `0.138.x`. +- Moved future public versions to the PolyForm Noncommercial License 1.0.0 + while preserving historical Apache License 2.0 terms for earlier versions. + +## Breaking Changes + +- License change for future public versions: commercial use now requires a + separate written commercial license from Gale. +- No Swift API breaking changes are intended in this patch release. + +## Migration And Upgrade Notes + +- Consumers should use Codex CLI `0.138.x`. +- Commercial users should review [COMMERCIAL-USE.md](../../COMMERCIAL-USE.md) + and contact Gale for licensing. +- Earlier published SwiftASB versions remain available under the license terms + that applied to those versions. + +## Verification + +- Pending final release gate on the release branch. From 25dea4dc76c77532549e5dac91d4cf22dfb4f38c Mon Sep 17 00:00:00 2001 From: Gale W Date: Tue, 9 Jun 2026 21:42:02 -0400 Subject: [PATCH 4/5] release: use checked-in release notes --- docs/releases/v1.7.2.md | 8 +++++++- scripts/repo-maintenance/lib/common.sh | 17 +++++++++++++++++ scripts/repo-maintenance/release.sh | 16 +++++++++++++--- .../release/40-github-release.sh | 16 +++++++++++++--- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/docs/releases/v1.7.2.md b/docs/releases/v1.7.2.md index c261695..c6be136 100644 --- a/docs/releases/v1.7.2.md +++ b/docs/releases/v1.7.2.md @@ -27,4 +27,10 @@ ## Verification -- Pending final release gate on the release branch. +- `swift build` +- `swift test` +- `uv run pytest` from `Tools/AgentSB` +- `bash scripts/repo-maintenance/validate-all.sh` +- `git diff --check` +- Release flow now prefers this checked-in release-note file when creating the + GitHub release. diff --git a/scripts/repo-maintenance/lib/common.sh b/scripts/repo-maintenance/lib/common.sh index b0afa95..969c494 100755 --- a/scripts/repo-maintenance/lib/common.sh +++ b/scripts/repo-maintenance/lib/common.sh @@ -83,6 +83,23 @@ github_release_create_prerelease_flag() { fi } +github_release_notes_file() { + tag_name="$1" + release_version="${tag_name#v}" + + for candidate in \ + "$REPO_ROOT/docs/releases/$tag_name.md" \ + "$REPO_ROOT/docs/releases/$release_version.md" + do + if [ -f "$candidate" ]; then + printf '%s\n' "$candidate" + return 0 + fi + done + + return 1 +} + verify_github_release_prerelease_metadata() { tag_name="$1" expected_value="$(expected_github_prerelease_value "$tag_name")" diff --git a/scripts/repo-maintenance/release.sh b/scripts/repo-maintenance/release.sh index 7151e19..915f6de 100755 --- a/scripts/repo-maintenance/release.sh +++ b/scripts/repo-maintenance/release.sh @@ -399,7 +399,11 @@ create_github_release() { if [ "$REPO_MAINTENANCE_DRY_RUN" = "true" ]; then prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" - log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag${prerelease_flag:+ $prerelease_flag}." + if notes_file="$(github_release_notes_file "$RELEASE_TAG")"; then + log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag --notes-file $notes_file${prerelease_flag:+ $prerelease_flag}." + else + log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag --generate-notes${prerelease_flag:+ $prerelease_flag}." + fi return 0 fi @@ -410,8 +414,14 @@ create_github_release() { fi prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" - # shellcheck disable=SC2086 - gh release create "$RELEASE_TAG" --verify-tag --generate-notes $prerelease_flag + if notes_file="$(github_release_notes_file "$RELEASE_TAG")"; then + # shellcheck disable=SC2086 + gh release create "$RELEASE_TAG" --verify-tag --notes-file "$notes_file" $prerelease_flag + else + warn "No release notes found at docs/releases/$RELEASE_TAG.md or docs/releases/${RELEASE_TAG#v}.md; falling back to GitHub-generated notes." + # shellcheck disable=SC2086 + gh release create "$RELEASE_TAG" --verify-tag --generate-notes $prerelease_flag + fi log "Created GitHub release $RELEASE_TAG." wait_for_github_release "$RELEASE_TAG" verify_github_release_prerelease_metadata "$RELEASE_TAG" diff --git a/scripts/repo-maintenance/release/40-github-release.sh b/scripts/repo-maintenance/release/40-github-release.sh index 2220e09..d365bcf 100755 --- a/scripts/repo-maintenance/release/40-github-release.sh +++ b/scripts/repo-maintenance/release/40-github-release.sh @@ -17,7 +17,11 @@ fi if [ "${REPO_MAINTENANCE_DRY_RUN:-false}" = "true" ]; then prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" - log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag${prerelease_flag:+ $prerelease_flag}." + if notes_file="$(github_release_notes_file "$RELEASE_TAG")"; then + log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag --notes-file $notes_file${prerelease_flag:+ $prerelease_flag}." + else + log "Would create a GitHub release for $RELEASE_TAG with gh release create --verify-tag --generate-notes${prerelease_flag:+ $prerelease_flag}." + fi exit 0 fi @@ -28,8 +32,14 @@ if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then fi prerelease_flag="$(github_release_create_prerelease_flag "$RELEASE_TAG")" -# shellcheck disable=SC2086 -gh release create "$RELEASE_TAG" --verify-tag --generate-notes $prerelease_flag +if notes_file="$(github_release_notes_file "$RELEASE_TAG")"; then + # shellcheck disable=SC2086 + gh release create "$RELEASE_TAG" --verify-tag --notes-file "$notes_file" $prerelease_flag +else + warn "No release notes found at docs/releases/$RELEASE_TAG.md or docs/releases/${RELEASE_TAG#v}.md; falling back to GitHub-generated notes." + # shellcheck disable=SC2086 + gh release create "$RELEASE_TAG" --verify-tag --generate-notes $prerelease_flag +fi log "Created GitHub release $RELEASE_TAG." wait_for_github_release "$RELEASE_TAG" verify_github_release_prerelease_metadata "$RELEASE_TAG" From e853aacc581d161eb70767a988dd3d68f6380efb Mon Sep 17 00:00:00 2001 From: Gale W Date: Tue, 9 Jun 2026 21:59:42 -0400 Subject: [PATCH 5/5] release: address v1.7.2 review findings --- README.md | 2 +- Sources/ASBAppKit/ASBThreadSidebarView.swift | 40 +++++++++---------- .../SwiftASB/History/ThreadHistoryStore.swift | 2 +- .../Public/CodexAppServer+Compatibility.swift | 4 +- .../Public/CodexAppServer+WireMapping.swift | 34 +++++++++++++++- .../Public/CodexAppServerTests.swift | 10 +++++ 6 files changed, 66 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 87d2548..073d390 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Listen to the SwiftASB Codex apps promo clip: ### Status -SwiftASB is actively maintained and supported by Gale. Our current API is v1, and `v1.7.2` is the current and latest release. +SwiftASB is actively maintained and supported by Gale. Our current API is v1, and `v1.7.2` is being prepared as the next release. ### What This Project Is diff --git a/Sources/ASBAppKit/ASBThreadSidebarView.swift b/Sources/ASBAppKit/ASBThreadSidebarView.swift index dd7b527..62014c9 100644 --- a/Sources/ASBAppKit/ASBThreadSidebarView.swift +++ b/Sources/ASBAppKit/ASBThreadSidebarView.swift @@ -192,44 +192,44 @@ extension ASBThreadSidebarView: NSOutlineViewDelegate { /// Snapshot adapter that owns AppKit tree mechanics for the sidebar. @MainActor -public final class ASBThreadSidebarAdapter { - public static let columnIdentifier = NSUserInterfaceItemIdentifier("ASBThreadSidebarColumn") +final class ASBThreadSidebarAdapter { + static let columnIdentifier = NSUserInterfaceItemIdentifier("ASBThreadSidebarColumn") - public private(set) var sections: [ASBThreadSidebarRow] = [] + private(set) var sections: [ASBThreadSidebarRow] = [] - public init(snapshot: ThreadSidebarSnapshot = .init()) { + init(snapshot: ThreadSidebarSnapshot = .init()) { apply(snapshot) } - public func apply(_ snapshot: ThreadSidebarSnapshot) { + func apply(_ snapshot: ThreadSidebarSnapshot) { sections = snapshot.sections.map(ASBThreadSidebarRow.init(section:)) } - public var flattenedRows: [ASBThreadSidebarRow] { + var flattenedRows: [ASBThreadSidebarRow] { sections.flatMap { [$0] + $0.children } } - public func row(forThreadID threadID: String) -> ASBThreadSidebarRow? { + func row(forThreadID threadID: String) -> ASBThreadSidebarRow? { sections.lazy .flatMap(\.children) .first { $0.threadItem?.id == threadID } } - public func numberOfChildren(of item: Any?) -> Int { + func numberOfChildren(of item: Any?) -> Int { guard let row = item as? ASBThreadSidebarRow else { return sections.count } return row.children.count } - public func child(_ index: Int, of item: Any?) -> ASBThreadSidebarRow { + func child(_ index: Int, of item: Any?) -> ASBThreadSidebarRow { guard let row = item as? ASBThreadSidebarRow else { return sections[index] } return row.children[index] } - public func isExpandable(_ item: Any) -> Bool { + func isExpandable(_ item: Any) -> Bool { guard let row = item as? ASBThreadSidebarRow else { return false } return !row.children.isEmpty } @@ -237,20 +237,20 @@ public final class ASBThreadSidebarAdapter { /// One AppKit outline row derived from a presentation snapshot. @MainActor -public final class ASBThreadSidebarRow: NSObject, Identifiable { - public enum Kind: Sendable, Equatable { +final class ASBThreadSidebarRow: NSObject, Identifiable { + enum Kind: Sendable, Equatable { case section case thread } - public let id: String - public let kind: Kind - public let title: String - public let subtitle: String? - public let threadItem: ThreadSidebarItem? - public let children: [ASBThreadSidebarRow] + let id: String + let kind: Kind + let title: String + let subtitle: String? + let threadItem: ThreadSidebarItem? + let children: [ASBThreadSidebarRow] - public init(section: ThreadSidebarSection) { + init(section: ThreadSidebarSection) { self.id = "section:\(section.id)" self.kind = .section self.title = section.title @@ -260,7 +260,7 @@ public final class ASBThreadSidebarRow: NSObject, Identifiable { super.init() } - public init(item: ThreadSidebarItem) { + init(item: ThreadSidebarItem) { self.id = "thread:\(item.id)" self.kind = .thread self.title = item.title diff --git a/Sources/SwiftASB/History/ThreadHistoryStore.swift b/Sources/SwiftASB/History/ThreadHistoryStore.swift index c73e267..6704626 100644 --- a/Sources/SwiftASB/History/ThreadHistoryStore.swift +++ b/Sources/SwiftASB/History/ThreadHistoryStore.swift @@ -1193,7 +1193,7 @@ actor ThreadHistoryStore { defaults.instructionSourcesData = try? encode(session.instructionSources) defaults.model = session.model defaults.modelProvider = session.modelProvider - defaults.reasoningEffort = session.reasoningEffort?.rawValue + defaults.reasoningEffort = session.reasoningEffort?.wireValue defaults.sandboxPolicyData = try? encode(SandboxPolicySnapshot(session.sandboxPolicy)) defaults.serviceTier = session.serviceTier?.rawValue } diff --git a/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift b/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift index 2babe20..d209863 100644 --- a/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift +++ b/Sources/SwiftASB/Public/CodexAppServer+Compatibility.swift @@ -71,13 +71,13 @@ extension CodexAppServer { } /// Reasoning effort option passed to Codex requests. - public enum ReasoningEffort: String, Sendable, Equatable { + public enum ReasoningEffort: Sendable, Equatable { case high case low case medium case minimal case none - case unrecognized + case unrecognized(String) case xhigh } diff --git a/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift b/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift index 0af0b06..0cc2a03 100644 --- a/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift +++ b/Sources/SwiftASB/Public/CodexAppServer+WireMapping.swift @@ -488,7 +488,22 @@ extension CodexAppServer.SessionStartSource { extension CodexAppServer.ReasoningEffort { init(wireValue: String) { - self = Self(rawValue: wireValue) ?? .unrecognized + switch wireValue { + case "high": + self = .high + case "low": + self = .low + case "medium": + self = .medium + case "minimal": + self = .minimal + case "none": + self = .none + case "xhigh": + self = .xhigh + default: + self = .unrecognized(wireValue) + } } init?(wireValue: String?) { @@ -497,7 +512,22 @@ extension CodexAppServer.ReasoningEffort { } var wireValue: String { - rawValue + switch self { + case .high: + "high" + case .low: + "low" + case .medium: + "medium" + case .minimal: + "minimal" + case .none: + "none" + case let .unrecognized(value): + value + case .xhigh: + "xhigh" + } } } diff --git a/Tests/SwiftASBTests/Public/CodexAppServerTests.swift b/Tests/SwiftASBTests/Public/CodexAppServerTests.swift index 1f77360..dd17286 100644 --- a/Tests/SwiftASBTests/Public/CodexAppServerTests.swift +++ b/Tests/SwiftASBTests/Public/CodexAppServerTests.swift @@ -4,6 +4,16 @@ import Testing @Suite("CodexAppServer", .serialized) struct CodexAppServerTests { + @Test("preserves unknown reasoning effort wire values") + func preservesUnknownReasoningEffortWireValues() { + let effort = CodexAppServer.ReasoningEffort(wireValue: "experimental-ludicrous") + + #expect(effort == .unrecognized("experimental-ludicrous")) + #expect(effort.wireValue == "experimental-ludicrous") + #expect(CodexAppServer.ReasoningEffort(wireValue: "medium") == .medium) + #expect(CodexAppServer.ReasoningEffort.medium.wireValue == "medium") + } + @Test("describes public app-server errors with operation context") func describesPublicAppServerErrorsWithOperationContext() { let invalidState = CodexAppServerError.invalidState(reason: "Initialize the app-server before starting a thread.")