diff --git a/apps/flutter/lib/src/client/app_state.dart b/apps/flutter/lib/src/client/app_state.dart index af5476b3..afadfc4a 100644 --- a/apps/flutter/lib/src/client/app_state.dart +++ b/apps/flutter/lib/src/client/app_state.dart @@ -188,12 +188,14 @@ final class ComposerSlashCommand { required this.name, required this.description, required this.insert, + this.aliases = const [], this.disabledReason, }); final String name; final String description; final String insert; + final List aliases; final String? disabledReason; } diff --git a/apps/flutter/lib/src/client/t4_client_controller.dart b/apps/flutter/lib/src/client/t4_client_controller.dart index fb499192..c6da99da 100644 --- a/apps/flutter/lib/src/client/t4_client_controller.dart +++ b/apps/flutter/lib/src/client/t4_client_controller.dart @@ -187,7 +187,9 @@ final class T4ClientController extends ChangeNotifier implements T4Actions { if (session == null) return const SessionComposerState(); final choices = []; final seen = {}; - final slashCommands = []; + final slashCommands = {}; + final operationCapabilities = + _catalogFrame?.operations ?? const []; for (final item in _catalogItems) { if (item.kind == 'model') { final selector = modelItemSelector(item); @@ -205,7 +207,7 @@ final class T4ClientController extends ChangeNotifier implements T4Actions { ); continue; } - if (item.kind != 'command') continue; + if (item.kind != 'command' || operationCapabilities.isNotEmpty) continue; final bareName = item.name.replaceFirst(RegExp(r'^/+'), ''); final missingCapability = item.capabilities ?.where((capability) => !_grantedCapabilities.contains(capability)) @@ -215,13 +217,54 @@ final class T4ClientController extends ChangeNotifier implements T4Actions { : missingCapability == null ? null : 'Not granted on this host'; - slashCommands.add( - ComposerSlashCommand( - name: '/$bareName', - description: item.description ?? '', - insert: '/$bareName ', - disabledReason: disabledReason, - ), + final name = '/$bareName'; + slashCommands[name] = ComposerSlashCommand( + name: name, + description: item.description ?? '', + insert: '$name ', + disabledReason: disabledReason, + ); + } + for (final operation in operationCapabilities) { + if (!operation.operationId.startsWith('slash.')) continue; + final bareName = operation.operationId.substring('slash.'.length); + if (bareName.isEmpty) continue; + final name = '/$bareName'; + final metadata = operation.raw['metadata']; + final rawAliases = metadata is Map + ? metadata['aliases'] + : null; + final aliases = rawAliases is List + ? rawAliases + .whereType() + .where((alias) => alias.isNotEmpty) + .map((alias) => '/${alias.replaceFirst(RegExp(r'^/+'), '')}') + .toList(growable: false) + : const []; + final missingCapability = operation.capabilities + ?.where((capability) => !_grantedCapabilities.contains(capability)) + .firstOrNull; + String? disabledReason; + if (!operation.supported) { + disabledReason = + operation.disabledReason?.message ?? 'Not available on this host'; + } else if (missingCapability != null) { + disabledReason = missingCapability == 'terminal.io' + ? 'Needs terminal access on this host' + : 'Not granted on this host'; + } else if ((session.turnActive || _submitting) && + bareName == 'compact') { + disabledReason = 'Wait for the turn to finish'; + } else if ((session.turnActive || _submitting) && + bareName == 'retry') { + disabledReason = 'A turn is already running'; + } + slashCommands[name] = ComposerSlashCommand( + name: name, + aliases: List.unmodifiable(aliases), + description: operation.description ?? '', + insert: '$name ', + disabledReason: disabledReason, ); } final groups = groupModelChoices(_catalogItems); @@ -258,7 +301,9 @@ final class T4ClientController extends ChangeNotifier implements T4Actions { modelSelector: session.modelSelector, modelChoices: List.unmodifiable(choices), modelGroups: List.unmodifiable(groups), - slashCommands: List.unmodifiable(slashCommands), + slashCommands: List.unmodifiable( + slashCommands.values, + ), thinking: session.thinking, thinkingLevels: List.unmodifiable(levels), fastEnabled: session.fast, diff --git a/apps/flutter/lib/src/ui/conversation_pane.dart b/apps/flutter/lib/src/ui/conversation_pane.dart index a81ed736..478ac48c 100644 --- a/apps/flutter/lib/src/ui/conversation_pane.dart +++ b/apps/flutter/lib/src/ui/conversation_pane.dart @@ -1118,7 +1118,11 @@ final class _PromptComposerState extends State<_PromptComposer> { ? const [] : composer.slashCommands .where( - (command) => command.name.toLowerCase().contains(slashQuery), + (command) => + command.name.toLowerCase().contains(slashQuery) || + command.aliases.any( + (alias) => alias.toLowerCase().contains(slashQuery), + ), ) .take(5) .toList(growable: false); @@ -1157,7 +1161,10 @@ final class _PromptComposerState extends State<_PromptComposer> { enabled: command.disabledReason == null, title: Text(command.name), subtitle: Text( - command.disabledReason ?? command.description, + command.disabledReason ?? + (command.aliases.isEmpty + ? command.description + : '${command.description} · ${command.aliases.join(' ')}'), ), onTap: command.disabledReason == null ? () => _selectSlashCommand(command) diff --git a/apps/flutter/test/client/t4_client_controller_test.dart b/apps/flutter/test/client/t4_client_controller_test.dart index a0a2ca14..c1ece049 100644 --- a/apps/flutter/test/client/t4_client_controller_test.dart +++ b/apps/flutter/test/client/t4_client_controller_test.dart @@ -459,6 +459,103 @@ void main() { }, ); + test( + 'composer uses official OMP operation capabilities and keeps terminal-only commands disabled', + () async { + final profile = _profile('alpha'); + final connector = _FakeConnector(); + final controller = _controller( + _MemoryDirectoryStore( + directory: const HostDirectory.empty().upsert(profile), + ), + _MemoryCredentialStore(), + connector, + ); + addTearDown(controller.dispose); + await controller.initialize(); + final channel = connector.channels.single; + + channel.emit( + _welcome( + 'host-alpha', + capabilities: const [ + 'sessions.read', + 'sessions.prompt', + 'catalog.read', + ], + features: const ['catalog.metadata'], + ), + ); + await _flush(); + final list = channel.sentJson.firstWhere( + (frame) => frame['command'] == 'session.list', + ); + channel.emit( + _response( + list, + command: 'session.list', + result: _sessionListResult('host-alpha'), + ), + ); + channel.emit({ + 'v': 'omp-app/1', + 'type': 'catalog', + 'hostId': 'host-alpha', + 'revision': 'catalog-operation-capabilities', + 'items': [ + { + 'id': 'command:session.cancel', + 'kind': 'command', + 'name': 'session.cancel', + }, + ], + 'operations': [ + { + 'operationId': 'session.prompt', + 'label': 'Prompt', + 'execution': 'typed', + 'supported': true, + }, + { + 'operationId': 'slash.compact', + 'label': '/compact', + 'description': 'Compact the active conversation', + 'execution': 'headless', + 'supported': true, + 'capabilities': ['sessions.prompt'], + 'metadata': { + 'aliases': ['compress'], + }, + }, + { + 'operationId': 'slash.plan', + 'label': '/plan', + 'description': 'Toggle plan mode', + 'execution': 'terminal-only', + 'supported': false, + 'disabledReason': { + 'code': 'terminal_only', + 'message': '/plan requires the OMP terminal interface.', + }, + }, + ], + }); + await _flush(); + + final commands = controller.state.composer.slashCommands; + expect(commands.map((command) => command.name), [ + '/compact', + '/plan', + ]); + expect(commands.first.aliases, ['/compress']); + expect(commands.first.disabledReason, isNull); + expect( + commands.last.disabledReason, + '/plan requires the OMP terminal interface.', + ); + }, + ); + test('command ids are unique across controller restarts', () async { final profile = _profile('alpha'); final directory = _MemoryDirectoryStore( diff --git a/apps/flutter/test/ui/host_flow_test.dart b/apps/flutter/test/ui/host_flow_test.dart index 99f78de0..38dcf1ac 100644 --- a/apps/flutter/test/ui/host_flow_test.dart +++ b/apps/flutter/test/ui/host_flow_test.dart @@ -811,6 +811,69 @@ void main() { expect(onLabel.style?.color, isNull); }, ); + + testWidgets( + 'slash menu finds aliases and explains terminal-only commands', + (tester) async { + final profile = HostProfile.parseTailnetAddress( + 'https://alpha.tailnet-name.ts.net', + ); + final state = T4ViewState( + connectionPhase: ConnectionPhase.ready, + hostDirectory: HostDirectory.empty().upsert(profile), + authenticationPhase: AuthenticationPhase.paired, + grantedCapabilities: t4RequestedCapabilities.toSet(), + selectedSessionId: 'session-alpha', + sessions: const [ + SessionSummary( + hostId: 'host-alpha', + sessionId: 'session-alpha', + projectId: 'project-alpha', + projectName: 'Project Alpha', + title: 'Capability-aware slash menu', + revision: 'revision-alpha', + status: 'idle', + ), + ], + composer: const SessionComposerState( + slashCommands: [ + ComposerSlashCommand( + name: '/compact', + aliases: ['/compress'], + description: 'Compact the active conversation', + insert: '/compact ', + ), + ComposerSlashCommand( + name: '/plan', + description: 'Toggle plan mode', + insert: '/plan ', + disabledReason: '/plan requires the OMP terminal interface.', + ), + ], + ), + ); + + await pumpApp( + tester, + state: state, + actions: _FakeActions(), + size: compactPhone, + ); + await tester.enterText(find.byType(TextField).last, '/'); + await tester.pump(); + expect(find.text('/compact'), findsOneWidget); + expect(find.text('/plan'), findsOneWidget); + expect( + find.text('/plan requires the OMP terminal interface.'), + findsOneWidget, + ); + + await tester.enterText(find.byType(TextField).last, '/compress'); + await tester.pump(); + expect(find.text('/compact'), findsOneWidget); + expect(find.text('/plan'), findsNothing); + }, + ); testWidgets('keeps the latest message visible when the keyboard opens', ( tester, ) async { diff --git a/apps/web/src/features/composer/slash.ts b/apps/web/src/features/composer/slash.ts index b4d6b295..b7616675 100644 --- a/apps/web/src/features/composer/slash.ts +++ b/apps/web/src/features/composer/slash.ts @@ -5,7 +5,7 @@ // f61fa9499d96fee825492aba204593c37b27e0cb). OMP changes: fixture-fed // catalog with aliases, argument hints, and capability-gated disabled // reasons instead of provider commands. -import type { CatalogItem } from "@t4-code/protocol"; +import type { CatalogItem, OperationCapability } from "@t4-code/protocol"; import { scoreQueryMatch } from "./match.ts"; export interface SlashCommand { @@ -42,6 +42,7 @@ export function slashCommandsFromCatalog( items: readonly CatalogItem[], context: SlashCatalogContext, granted: readonly string[], + operations: readonly OperationCapability[] = [], ): SlashCommand[] { const offlineReason = context.link === "cached" @@ -49,25 +50,71 @@ export function slashCommandsFromCatalog( : context.link === "offline" ? "Unavailable while the host is unreachable" : null; - const commands: SlashCommand[] = []; - for (const item of items) { - if (item.kind !== "command") continue; - const bareName = item.name.replace(/^\/+/, ""); + const commands = new Map(); + // Operation capabilities are the authoritative official-OMP command list. + // Fall back to legacy command items only when an older host does not expose + // the new contract at all; otherwise typed commands such as session.cancel + // would be mistaken for slash commands. + if (operations.length === 0) { + for (const item of items) { + if (item.kind !== "command") continue; + const bareName = item.name.replace(/^\/+/, ""); + const name = `/${bareName}`; + const metadata = item.metadata ?? {}; + const rawAliases = Array.isArray(metadata.aliases) ? metadata.aliases : []; + const aliases = rawAliases + .filter((alias): alias is string => typeof alias === "string" && alias !== "") + .map((alias) => `/${alias.replace(/^\/+/, "")}`); + const argsHint = typeof metadata.inlineHint === "string" ? metadata.inlineHint : ""; + const missingCapability = (item.capabilities ?? []).find( + (capability) => !granted.includes(capability), + ); + const disabledReason = + offlineReason ?? + context.readOnlyReason ?? + (item.supported === false + ? (item.reason ?? "Not available on this host") + : missingCapability !== undefined + ? missingCapability === "terminal.io" + ? "Needs terminal access on this host" + : "Not granted on this host" + : context.turnActive && bareName === "compact" + ? "Wait for the turn to finish" + : context.turnActive && bareName === "retry" + ? "A turn is already running" + : null); + commands.set(name, { + name, + aliases, + description: item.description ?? "", + argsHint, + disabledReason, + insert: `${name} `, + }); + } + } + for (const operation of operations) { + const operationId = String(operation.operationId); + if (!operationId.startsWith("slash.")) continue; + const bareName = operationId.slice("slash.".length); + if (bareName === "") continue; const name = `/${bareName}`; - const metadata = item.metadata ?? {}; + const metadata = + operation.metadata !== null && typeof operation.metadata === "object" + ? (operation.metadata as Record) + : {}; const rawAliases = Array.isArray(metadata.aliases) ? metadata.aliases : []; const aliases = rawAliases .filter((alias): alias is string => typeof alias === "string" && alias !== "") .map((alias) => `/${alias.replace(/^\/+/, "")}`); - const argsHint = typeof metadata.inlineHint === "string" ? metadata.inlineHint : ""; - const missingCapability = (item.capabilities ?? []).find( + const missingCapability = (operation.capabilities ?? []).find( (capability) => !granted.includes(capability), ); const disabledReason = offlineReason ?? context.readOnlyReason ?? - (item.supported === false - ? (item.reason ?? "Not available on this host") + (!operation.supported + ? (operation.disabledReason?.message ?? "Not available on this host") : missingCapability !== undefined ? missingCapability === "terminal.io" ? "Needs terminal access on this host" @@ -77,16 +124,16 @@ export function slashCommandsFromCatalog( : context.turnActive && bareName === "retry" ? "A turn is already running" : null); - commands.push({ + commands.set(name, { name, aliases, - description: item.description ?? "", - argsHint, + description: operation.description ?? "", + argsHint: "", disabledReason, insert: `${name} `, }); } - return commands; + return [...commands.values()]; } /** diff --git a/apps/web/src/features/session-runtime/live-runtime.ts b/apps/web/src/features/session-runtime/live-runtime.ts index 0b6a9f5f..b2bb0184 100644 --- a/apps/web/src/features/session-runtime/live-runtime.ts +++ b/apps/web/src/features/session-runtime/live-runtime.ts @@ -1215,6 +1215,7 @@ export function createLiveSessionRuntime(options: LiveRuntimeOptions): SessionRu readOnlyReason: controlGate === null ? null : controlGate.slashReason, }, granted, + catalog.operations ?? [], ), contextUsedTokens: contextUsage?.used ?? 0, contextWindowTokens: contextUsage?.limit ?? 0, diff --git a/apps/web/test/live-composer.test.ts b/apps/web/test/live-composer.test.ts index 31359849..e22fd5c3 100644 --- a/apps/web/test/live-composer.test.ts +++ b/apps/web/test/live-composer.test.ts @@ -29,6 +29,7 @@ import { type DurableEntry, type DurableEntryFrame, type LiveEventFrame, + type OperationCapability, type SessionSnapshotFrame, type SessionsFrame, } from "@t4-code/protocol"; @@ -149,8 +150,19 @@ function commandItem(name: string, capabilities?: readonly string[]): CatalogIte }; } -function catalogFrame(rev: string, items: CatalogItem[]): CatalogFrame { - return { v: V, type: "catalog", hostId: hostId(HOST), revision: revision(rev), items }; +function catalogFrame( + rev: string, + items: CatalogItem[], + operations?: OperationCapability[], +): CatalogFrame { + return { + v: V, + type: "catalog", + hostId: hostId(HOST), + revision: revision(rev), + items, + ...(operations === undefined ? {} : { operations }), + }; } function pendingPromptSessionsFrame( @@ -668,6 +680,51 @@ describe("stop affordance and slash catalog", () => { ); expect(commands.find((command) => command.name === "/review")?.disabledReason).toBeNull(); }); + + it("uses official OMP operation capabilities instead of mistaking typed commands for slash commands", async () => { + const { shell, runtime } = await startedRuntime(); + shell.emitFrame({ + targetId: "local", + frame: catalogFrame( + "official-operations", + [commandItem("session.cancel")], + [ + { + operationId: "session.prompt" as OperationCapability["operationId"], + label: "Prompt", + execution: "typed", + supported: true, + }, + { + operationId: "slash.compact" as OperationCapability["operationId"], + label: "/compact", + description: "Compact the active conversation", + execution: "headless", + supported: true, + capabilities: ["sessions.prompt"], + metadata: { aliases: ["compress"] }, + }, + { + operationId: "slash.plan" as OperationCapability["operationId"], + label: "/plan", + description: "Toggle plan mode", + execution: "terminal-only", + supported: false, + disabledReason: { + code: "terminal_only", + message: "/plan requires the OMP terminal interface.", + }, + }, + ], + ), + }); + + const commands = runtime.getSnapshot().slashCommands ?? []; + expect(commands.map((command) => command.name)).toEqual(["/compact", "/plan"]); + expect(commands[0]?.aliases).toEqual(["/compress"]); + expect(commands[0]?.disabledReason).toBeNull(); + expect(commands[1]?.disabledReason).toBe("/plan requires the OMP terminal interface."); + }); }); describe("confirmations", () => { diff --git a/docs/OMP_T4_CAPABILITY_AUDIT.md b/docs/OMP_T4_CAPABILITY_AUDIT.md new file mode 100644 index 00000000..745fdc1d --- /dev/null +++ b/docs/OMP_T4_CAPABILITY_AUDIT.md @@ -0,0 +1,328 @@ +# OMP to T4 Capability Audit + +**Audit date:** 2026-07-20, refreshed 2026-07-21 after T4 PRs #109, #111, #113, and #114 + +**Scope:** source-level audit of original OMP, Lycaon's T4 fork, T4 desktop, the responsive web/Capacitor mobile client, and the new Flutter client + +**Companion tracker:** [`OMP_T4_CAPABILITY_TRACKER.csv`](./OMP_T4_CAPABILITY_TRACKER.csv) + +## Executive result + +T4 already has the right basic architecture: T4 owns the app-facing protocol and host service, while OMP remains the authority for agent sessions, tools, models, settings, credentials, workers, locks, compaction, and OMP-native events. + +The largest problem is no longer a missing transport layer. T4 now has a shared operation-capability contract and a T4-owned adapter that asks unmodified official OMP which commands it can run headlessly. The remaining work is to carry that truth into every client and then replace the most important terminal-only workflows with typed app actions. + +1. **Operation truth is implemented in the host but was not yet reaching the UI.** PR #111 added `typed`, `headless`, `terminal-only`, and `unavailable`; PR #113 classifies stock OMP commands and rejects known terminal-only text before it reaches the model. This sprint carries that result into desktop/web and Flutter. +2. **Important daily workflows still lack a typed app action:** plan and goal modes, session branch/fork/tree, handoff, provider login/logout, queue control, and child-agent steering. +3. **The official-OMP path is real but Gate 0 is not complete.** Restart continuity is tested on macOS, while Linux execution, steering/follow-up, approval, cancellation, and ambiguous dispatch-crash behavior still need proof. +4. **The Lycaon fork is transitional, not the desired product center.** The current public package still pins its thin authority bridge, but new compatibility work should prefer the T4-owned official-OMP adapter and add only small, extractable bridge methods when stock OMP has no usable seam. +5. **Protocol vocabulary is not UI coverage.** Every tracker row needs an explicit client disposition: direct control, palette action, read-only view, disabled explanation, terminal handoff, or unavailable. +6. **Source is ahead of the public release.** Flutter and the latest adapter work are on `main`; the latest public GitHub release remains v0.1.28. + +## Current implementation checkpoint + +The truthful-command foundation is split cleanly between merged host work and this client sprint: + +- **Merged:** PR #111 defines one operation contract for every runtime and client. +- **Merged:** PR #113 queries official OMP's bounded `get_available_commands`, supplements omitted terminal-only commands from a pinned reviewed manifest, and blocks known terminal-only slash text before prompt dispatch. +- **Merged:** PR #114 proves restart continuity through the official adapter on macOS. +- **Merged:** PR #117 preserves `catalog.get.result.operations` through the desktop runtime. +- **This sprint:** web/Electron and Flutter build their slash menus from those capabilities; typed commands such as `session.cancel` are no longer mistaken for slash commands; unsupported entries remain visible and disabled with OMP's reason. +- **Already merged:** PR #106 provides confined project file search for the web/Electron Quick Open path. Flutter still needs its own Quick Open surface. + +The tracker distinguishes merged source, work in this sprint, and public release state. None of the new adapter/client work is claimed as packaged desktop, Android, or iOS proof yet. + +### Sprint 1 coverage matrix + +| Slice | Host/runtime | Desktop web/Electron | Mobile web/Capacitor | Flutter desktop/mobile | Evidence state | +|---|---|---|---|---|---| +| Operation capability contract | Merged | Decodes shared contract | Same web client | Dart decoder merged | PR #111 green | +| Official OMP command discovery and rejection | Merged | Receives through host | Receives through host | Receives through host | PR #113 green | +| Restart continuity | macOS proof merged | Shared host behavior | Shared host behavior | Shared host behavior | PR #114 green; other Gate 0 rows open | +| Preserve `catalog.get.operations` | Merged host response | Merged in PR #117 | Merged in PR #117 | Already decoded | Response and live-frame client tests pass | +| Capability-aware slash menu | Host rejects unsafe fallback | Implemented this sprint | Implemented this sprint | Implemented this sprint | Web tests pass; Flutter tests authored but local SDK is unavailable | +| Project Quick Open | `files.search` merged | Implemented on `main` | Implemented on `main` | Missing | PR #106 green; Flutter is next UI-only slice | + +This matrix is intentionally stricter than “the protocol supports it.” A row is complete only when the connected runtime advertises it, the client exposes an honest action or explanation, and the target package has been exercised. + +## Source pins and proof boundary + +| Source | Audited ref | Why it matters | +|---|---|---| +| Original OMP | [`can1357/oh-my-pi@89d6a8f6`](https://github.com/can1357/oh-my-pi/commit/89d6a8f6d14286f32f09ec9c8aa8af7b3451d2d6), version 17.0.6 | Current original product surface | +| Lycaon OMP fork | [`lyc-aon/oh-my-pi@8476f445`](https://github.com/lyc-aon/oh-my-pi/commit/8476f4451ed95c5d5401785d279a93d3c659fac4), tag [`t4code-17.0.5-appserver-10`](https://github.com/lyc-aon/oh-my-pi/releases/tag/t4code-17.0.5-appserver-10) | Current released thin authority bridge; transitional compatibility input | +| Shared upstream base | [`can1357/oh-my-pi@9fd6e971`](https://github.com/can1357/oh-my-pi/commit/9fd6e97113f5ed3a847e66d346970efdf8afcad9), version 17.0.5 | Last shared OMP point | +| T4 `main` | [`fcc67cb1`](https://github.com/LycaonLLC/t4-code/commit/fcc67cb1b91aae357c02e00b2f1670eb6961bc93), version 0.1.30 in source | Official-OMP classification, restart proof, and desktop capability preservation are merged | +| Flutter merge | [`LycaonLLC/t4-code#104`](https://github.com/LycaonLLC/t4-code/pull/104) | New shared desktop/mobile client now on `main` | +| Public T4 release | [`v0.1.28`](https://github.com/LycaonLLC/t4-code/releases/tag/v0.1.28) | Latest public release visible during the audit | + +This is primarily a source audit, supplemented by merged official-adapter smokes and focused TypeScript tests in this sprint. It does not prove that every path works in a packaged app. Live Flutter desktop, Android, iOS, web, and Electron round trips remain a separate verification pass. + +Relevant planning and implementation changes: + +- [#109: canonical local and managed architecture](https://github.com/LycaonLLC/t4-code/pull/109) — merged; makes the shared T4-owned official-OMP adapter the intended path. +- [#111: operation capability contract](https://github.com/LycaonLLC/t4-code/pull/111) — merged. +- [#113: official OMP operation classification](https://github.com/LycaonLLC/t4-code/pull/113) — merged. +- [#114: official OMP restart continuity](https://github.com/LycaonLLC/t4-code/pull/114) — merged. +- [#117: preserve OMP operation capabilities in the desktop runtime](https://github.com/LycaonLLC/t4-code/pull/117) — merged. +- [#98: standard OMP view-only compatibility](https://github.com/LycaonLLC/t4-code/pull/98) — still open, but its older read-only approach must not become a second long-term adapter beside the architecture in #109. + +## Status and priority language + +| Label | Meaning | +|---|---| +| `Code: full` | The audited source has a direct user path for the core behavior. It may still need packaged-app proof. | +| `Code: partial` | Some behavior exists, but important controls, detail, or platform coverage are missing. | +| `Slash only` | The app can use a verified headless slash command, but there is no first-class app action. | +| `Read only` | The app can display the state but cannot fully operate it. | +| `Missing` | No dependable app path was found. | +| `Platform` | The terminal presentation itself should not be copied; T4 needs an equivalent native behavior where useful. | +| `Open PR` | Work exists but is not merged into the audited `main`. | + +| Tier | Meaning | +|---|---| +| `T0` | Core daily loop, data safety, or a misleading behavior that should be fixed before claiming parity | +| `T1` | Frequent power-user workflow that materially improves normal desktop/mobile use | +| `T2` | Advanced integration or less frequent workflow | +| `T3` | Niche, diagnostic, or terminal-specific convenience | + +## What OMP actually contains + +OMP is much more than a chat loop. Its major surfaces are: + +| OMP area | Important behavior | +|---|---| +| Runtime entry points | Interactive terminal UI, one-shot print/JSON, Node SDK, RPC/RPC-UI, ACP editor integration, encrypted live collaboration, and browser guest mode | +| Prompt control | Prompt, live steering, after-turn follow-ups, queue modes, structured questions, pause, cancel, retry, force-tool, side questions, and background tangents | +| Sessions | Persistent JSONL trees, resume, branch, fork, tree navigation, rename, move, archive/delete, provider-state reset, compact, handoff, export, share, and collaboration | +| Models and providers | Provider login, model inventory, role routing, thinking levels, service tiers, fallbacks, provider health, and quota/reset data | +| Tools | Files, shell, eval, LSP, debugger, AST operations, browser, web search, GitHub, images, speech, agents, todos, memory, checkpoints, and rewind | +| Agents | Subagents, worktree isolation, progress, child transcripts, cancellation, jobs, hub coordination, and todos | +| Extensibility | Skills, extensions, plugins, marketplaces, custom commands, hooks, custom renderers/providers, and MCP servers/resources/prompts/OAuth | +| Context and memory | Context reporting, compaction strategies, project rules, stream rules, memory backends, retain/recall/reflect, checkpoint, and rewind | +| Developer operations | Files, diff/review, persistent terminal, one-shot bash, SSH, browser automation, worktrees, commits, reviews, and CI repair | +| Operations | Settings, approval policy, usage, statistics, debugging, changelog, install/update, completions, setup, and garbage collection | + +The [current OMP README](https://github.com/can1357/oh-my-pi/blob/89d6a8f6d14286f32f09ec9c8aa8af7b3451d2d6/README.md), [RPC documentation](https://github.com/can1357/oh-my-pi/blob/89d6a8f6d14286f32f09ec9c8aa8af7b3451d2d6/docs/rpc.md), [session-operation matrix](https://github.com/can1357/oh-my-pi/blob/89d6a8f6d14286f32f09ec9c8aa8af7b3451d2d6/docs/session-operations-export-share-fork-resume.md), [settings reference](https://github.com/can1357/oh-my-pi/blob/89d6a8f6d14286f32f09ec9c8aa8af7b3451d2d6/docs/settings.md), and [extension reference](https://github.com/can1357/oh-my-pi/blob/89d6a8f6d14286f32f09ec9c8aa8af7b3451d2d6/docs/extensions.md) are the primary upstream references. + +### Command and tool counts + +- Original OMP defines 62 built-in top-level slash commands before aliases and dynamically added skill commands. +- The Lycaon fork adds `/mechanism` and `/continue-in-t4`, bringing the fork catalog to 64. +- A live official-OMP smoke in PR #113 produced 68 normalized operations: one typed prompt operation plus the headless commands returned by official OMP and 35 reviewed terminal-only commands omitted from that headless feed. +- These counts are evidence for the pinned OMP build, not permanent constants. The client reads the operation list returned by the connected runtime rather than baking the count into its UI. +- The source registry contains 29 built-in tool names including hidden control tools. The desktop web client has specialized cards for 24; `checkpoint`, `rewind`, `memory_edit`, `learn`, and `manage_skill` currently use the safe generic fallback. Flutter currently uses generic structured tool cards rather than specialized per-tool cards. +- OMP's settings schema has 415 top-level settings; 303 include UI metadata. The fork already turns the schema into secret-safe catalog data, so T4 should keep generating settings UI instead of manually rebuilding hundreds of controls. + +## What Lycaon's fork adds today + +The fork provides the T4-specific bridge, launcher, compatibility packages, and two additional product surfaces: + +| Addition | Purpose | Long-term home | +|---|---|---| +| `omp bridge --stdio` | Exposes OMP-wide authority through `t4-omp-authority/1` | Thin OMP adapter in the fork | +| `omp appserver` compatibility command | Starts/administers the T4-owned host | Compatibility launcher only | +| `omp home` | Loopback web UI for profiles, routing, settings, roles, and providers | Evaluate separately; do not duplicate logic in every client | +| `omp mechanism` | Archived harness/session visualization | Optional advanced surface | +| `packages/app-wire` | Compatibility re-export of the T4 protocol | T4 owns the active protocol | +| `packages/appserver` | Compatibility wrapper for T4's host service | T4 owns the active host | + +Fork PR [#22](https://github.com/lyc-aon/oh-my-pi/pull/22) removed more than 37,000 lines of copied generic host/wire code from OMP and placed that ownership in T4. Fork PR [#23](https://github.com/lyc-aon/oh-my-pi/pull/23) added the smaller authority bridge. That reduction was correct, but PR #109 now sets a stronger direction: the normal local and managed paths should share a T4-owned adapter around pinned official OMP. The fork bridge remains a current compatibility input until Gate 0 proves the official path can replace each required authority safely. + +## Current T4 coverage + +### Strong foundations already present + +| Area | Current source evidence | +|---|---| +| Host and security | Versioned bounded protocol, device capabilities, pairing, replay, controller/prompt leases, confirmation challenges, and negotiated features | +| Sessions | List, create, attach, rename, archive, restore, close/delete, prompt, steer, follow-up, cancel, retry, compact, pause/resume in the wire, model/thinking/fast controls | +| Transcript | Durable entries, reconnect reconciliation, images, paging, search, surrounding context, attention summaries, and latest outcomes | +| Developer tools | Confined files, diff/review, one-shot bash, persistent terminal, preview navigation/input/capture, and audit events | +| Host-wide OMP truth | Session discovery/lifecycle, roots, locks, settings/catalog, provider broker status, usage, files, review, bash, and terminal through the authority bridge | +| Desktop web/Electron | Session library, composer, attention, agent view, files, review, terminal, browser preview, settings, transcript search, usage, and host management | +| Responsive web/Capacitor | Shares most web UI and supports remote T4 gateway use; native Android shell adds secure storage, updates, and speech support | +| Flutter on `main` | Host/pairing management, sessions, conversation, attention, developer surfaces, settings, transcript search, usage, model controls, and structured tool cards | + +### Important gaps + +| Priority | Gap | Why it matters | Best patch path | +|---|---|---|---| +| T0 | Official adapter Gate 0 is incomplete | A clean command catalog is not enough to prove all lifecycle and failure behavior | Finish Linux, steer/follow-up, approval, cancellation, and dispatch-crash scenarios | +| T0 | Client capability propagation is not on `main` | The host knows the truth, but current clients can drop or ignore it | Complete and merge this desktop/web/Flutter slice | +| T0 | Release state is ambiguous | Source says v0.1.30 while public GitHub release remains v0.1.28 | Separate `on main`, `verified package`, and `publicly released` in the tracker/release gate | +| T0 | Plan, goal, branch/fork/tree, handoff, and provider auth lack complete typed app flows | These are central OMP workflows, not decorative terminal features | Typed T4 commands backed by existing OMP RPC where possible | +| T1 | Queue and pause/resume controls are not consistently exposed | Cross-device control needs explicit, predictable behavior | Finish client UI over existing wire commands | +| T1 | Child agents can be viewed/cancelled but not fully steered or messaged | Agent orchestration is a major reason to use OMP | Add typed agent actions and OMP event projection | +| T1 | Flutter project Quick Open is missing | Confined project search is merged in the host and web client but not exposed in Flutter | Add a shared Flutter search state and compact/wide picker over `files.search` | +| T1 | Mobile tool output is mostly generic | It is safe, but harder to scan than desktop | Share semantic tool-view models, then use platform-specific layouts | +| T2 | MCP, skills, plugins, extensions, and marketplaces are mainly catalog/settings data | Advanced OMP setup still requires the terminal | Start read-only, then add bounded management actions | +| T2 | Memory, checkpoints, rewind, collaboration, and sharing lack first-class app flows | Useful but less common and higher-risk to expose casually | Typed commands with confirmation and clear results | + +## Truthful command execution + +The original defect was that a command name in a catalog did not prove that the connected runtime could execute it outside the terminal UI. That allowed a terminal-only command to look selectable and then arrive as ordinary model text. + +```text +catalog used to show /plan + | + v +user selects /plan in T4 + | + +-- real headless handler? no + | + v +text reaches the model as an ordinary prompt + | + v +OMP plan mode was not actually enabled +``` + +The merged operation contract now uses these execution values: + +```text +typed a first-class T4 operation +headless discovered from official OMP and safe through its RPC prompt path +terminal-only recognized, visible, and blocked from app prompt dispatch +unavailable known to the product but not exposed by this runtime +``` + +PR #113 enforces this at the host boundary. This sprint enforces it again at the presentation boundary: + +```text +official OMP get_available_commands + | + v +T4 adapter classifies operations + | + +-- headless ------> selectable in desktop and mobile + | + +-- terminal-only -> visible, disabled, exact reason shown + | + +-- unavailable ---> omitted from unrelated controls or shown disabled +``` + +The app must still keep the host-side rejection. A disabled menu is helpful feedback, not a security or correctness boundary. + +## Recommended thin-host architecture + +Use the architecture established in PR #109. T4 owns one adapter and uses it in local and managed deployments; official OMP remains the runtime authority. + +```text +Desktop or mobile UI + | + | typed, versioned omp-app/1 commands + v +T4 local service or T4 managed runtime + | + +-- shared T4 adapter ------------> pinned official OMP RPC + | + +-- temporary missing seam -------> thin Lycaon bridge method + | only where Gate 0 proves it is needed + | + +-- T4 product services ----------> pairing, replay, search, + previews, workspaces, adapters +``` + +### Ownership rules + +| Layer | Owns | Does not own | +|---|---|---| +| OMP original | Agent behavior, session truth, workers, tools, models, provider auth, settings, compaction, OMP events | T4 device pairing, mobile transport, T4 screen layout | +| Lycaon fork | Temporary, small OMP-specific seams still required by the released product, plus pinned release provenance | The default home for capability discovery, lifecycle policy, generic host code, protocol code, or client behavior | +| T4 host and adapter | App protocol, security, replay, capability normalization, projections, search/indexes, artifacts, preview, workspace lifecycle, and official-OMP process handling | Reimplementation of OMP session/model/tool rules | +| T4 clients | Layout, navigation, accessibility, native integrations, and user actions gated by negotiated capabilities | Guessing runtime truth or parsing private OMP files directly | + +### How each kind of gap should be fixed + +| Gap type | Preferred fix | +|---|---| +| A screen is missing but the wire already supports it | Client-only UI work | +| OMP RPC already supports the operation | Add a typed T4 command that forwards to the existing RPC request | +| The operation is OMP-wide rather than session-specific | First look for a stable official RPC/SDK seam; add one small extractable fork bridge method only when needed | +| The behavior is safe but uncommon | Keep a verified headless slash path temporarily | +| The behavior is terminal presentation | Build a native equivalent or use an explicit CLI handoff | +| Original OMP lacks a stable generic seam | Propose a small upstream RPC/SDK improvement that benefits any client | +| The fork differs only because it is old | Move the compatibility proof to pinned official OMP; do not create another fork workaround | + +Do not send a giant T4 product PR to original OMP. A suitable upstream contribution is small and generic—for example, a stable RPC operation or capability field useful to any client. T4-specific device permissions, mobile behavior, search projections, and host lifecycle belong in T4. + +## Capability manifest and drift control + +Persist a generated compatibility snapshot from the T4 official-OMP Gate 0 run. It should contain: + +- Official OMP version and exact commit; when the fallback bridge is exercised, its fork commit and tag too. +- Operations and aliases using `typed`, `headless`, `terminal-only`, or `unavailable`. +- Built-in tool names and relevant rendering metadata. +- Settings schema hash and UI-metadata count. +- RPC commands/events, T4 adapter coverage, and any remaining authority-bridge methods. +- Provider/auth capabilities and runtime roles. + +CI should compare the new manifest with the previously approved manifest. It should fail only when a capability was added, removed, or changed without a tracker classification. This catches drift without forcing the host to duplicate OMP internals. + +The release compatibility row should pin all of these together: + +```text +official OMP version + commit + + T4 adapter version + Gate 0 result + + optional Lycaon fallback commit + tag + + authority protocol version + + omp-app protocol version + + T4 host package hash + + desktop/mobile build version +``` + +## Suggested delivery order + +### Phase 0: finish the official-OMP foundation + +1. Merge capability-aware desktop/web and Flutter presentation. +2. Finish the open Gate 0 scenarios: Linux, steer/follow-up, approval, cancellation, and ambiguous dispatch-crash recovery. +3. Generate a compatibility snapshot from the official adapter smoke. +4. Track source, verified package, and public release status separately. + +### Phase 1: close the daily-workflow gaps + +1. Plan and goal modes. +2. Branch/fork/tree and handoff. +3. Provider login/logout and setup. +4. Queue controls plus visible pause/resume and manual compaction. +5. Flutter Quick Open plus consistent project/worktree context. +6. Child-agent steer, follow-up, and wake/message actions. + +### Phase 2: make desktop and mobile equally useful + +1. Share semantic tool-view models between desktop and Flutter. +2. Finish mobile layouts for files, review, terminal, and preview without copying desktop chrome. +3. Verify reconnect, pairing, offline transcript, attention, and approval flows on macOS, Android, and iOS. +4. Add accessibility, notifications, updates, and platform lifecycle proof. + +### Phase 3: advanced OMP administration + +1. MCP, skills, plugins, extensions, and marketplace management. +2. Memory maintenance, checkpoint, and rewind. +3. Share/collaboration and encrypted guest access. +4. OMP Home and Mechanism integration decisions. + +## How to use the tracker + +The companion CSV has 104 feature rows, each with one stable feature ID. Update a client column only when there is a named source path, test, screenshot, or live round-trip attached as evidence. Protocol vocabulary alone is not enough. + +For each future sprint: + +1. Filter to the highest incomplete tier. +2. Check whether the gap is UI-only, an existing RPC seam, an authority-bridge seam, a CLI handoff, or simple upstream drift. +3. Implement the smallest complete vertical slice across the required layers. +4. Verify desktop and mobile separately. +5. Record the exact release/build where the feature became available. + +## Audit limitations and next proof pass + +- This pass did not run packaged OMP or T4 binaries. +- It did not visually inspect every desktop, Android, or iOS screen. +- Optional protocol features may not be negotiated by every host instance. +- The shared adapter is currently an official-OMP seam, not proof that T4 is a generic multi-runtime product. +- OMP Home and Mechanism were cataloged but not judged as required T4 screens. +- TUI decoration, key bindings, status-line layout, and editor overlays should be judged by functional outcome, not pixel-for-pixel parity. + +The next audit pass should execute Tier 0 and Tier 1 rows against packaged Flutter desktop, Android, and iOS builds, plus the still-supported web/Electron client where applicable, and attach proof to this tracker. diff --git a/docs/OMP_T4_CAPABILITY_TRACKER.csv b/docs/OMP_T4_CAPABILITY_TRACKER.csv new file mode 100644 index 00000000..fd90dd72 --- /dev/null +++ b/docs/OMP_T4_CAPABILITY_TRACKER.csv @@ -0,0 +1,105 @@ +"ID","Tier","Area","Capability","Original OMP 17.0.6","Lycaon t4code/main","Desktop web/Electron","Mobile web/Capacitor","Flutter main","Gap classification","Recommended route","Evidence note" +"H01","T0","Host","Protocol and capability negotiation","RPC and ACP have typed requests; RPC has no explicit version field","Adds versioned omp-app/1 and t4-omp-authority/1","Code: full","Code: full","Code: full","No product gap; upstream RPC versioning would reduce adapter ambiguity","Keep T4 wire; consider a small generic upstream RPC version field","Clients must honor the features and permissions in the live welcome frame" +"H02","T0","Host","Local host discovery and lifecycle","CLI runtime","Adds T4 host compatibility launcher and OMP authority","Code: full","N/A: remote gateway client","Code: partial","Flutter cutover and packaged lifecycle proof remain","Platform adapter over T4-owned host lifecycle","Do not move generic lifecycle back into OMP" +"H03","T0","Host","Remote pairing and multiple hosts","Not an OMP product concern","T4 host pairing authority","Code: full","Code: full","Code: full","Needs packaged cross-platform proof","Client UI over existing T4 protocol","T4-owned feature; not an upstream parity item" +"H04","T0","Host","Reconnect replay and transcript reconciliation","RPC stream and session persistence","T4 host adds replay and durable projection","Code: full","Code: full","Code: full","Needs live interruption tests per platform","Verify existing protocol and clients","Protocol vocabulary is present" +"H05","T1","Host","Offline cached transcript","Persistent local sessions","T4 clients cache projected state","Code: partial","Code: partial","Code: partial","Offline read behavior needs a single documented contract","Client cache policy only","OMP remains the authority after reconnect" +"H06","T0","Host","Operate pinned unmodified official OMP through the shared adapter","Official OMP exposes RPC command/event seams","Fork bridge remains the released fallback","Gate 0: partial","Gate 0: partial","Gate 0: partial","Adapter runs and restart continuity is proven on macOS; Linux, steer/follow-up, approval, cancellation, and dispatch-crash cases remain","Finish Gate 0 in T4; keep PR #98 from becoming a second long-term adapter","PRs #109, #113, and #114 are merged; packaged cutover is not proven" +"H07","T1","Host","Continue from OMP terminal into T4","Terminal command can hand off","Adds /continue-in-t4","Code: full","Code: partial","Code: partial","Mobile landing and failure recovery need proof","Safe CLI handoff plus deep link/session ID","Fork-specific command is appropriate" +"H08","T1","Host","Continue from T4 into OMP terminal","Native terminal can resume a session","Same as upstream","Code: partial","Platform","Code: partial","No clear cross-platform handoff action","Explicit CLI handoff with copyable command","Do not emulate the terminal TUI inside the app" +"H09","T0","Host","Official OMP and fallback-bridge release alignment","Current original is 17.0.6","Released fallback bridge is pinned to 17.0.5","Code: partial","Code: partial","Code: partial","T4 now has a pinned official-OMP Gate 0 lane, but no generated compatibility snapshot or packaged cutover proof","Generate adapter compatibility snapshots and record fallback usage explicitly","Do not classify every fork difference as required product behavior" +"H10","T0","Host","Source vs package vs public release truth","OMP publishes v17.0.6","Fork publishes pinned appserver tag","Main says v0.1.30; public release is v0.1.28","Main says v0.1.30; public release is v0.1.28","Implemented on main; not public release","Release evidence is contradictory","Track on-main, packaged-verified, and publicly-released separately","Public GitHub state checked 2026-07-20" +"P01","T0","Projects","Project and session grouping","Sessions retain cwd and project metadata","Authority resolves project roots","Code: full","Code: full","Code: full","No major source gap","Keep projection in T4; roots remain OMP authority","Verify large libraries and moved projects" +"P02","T0","Projects","Create project or start session in a project","CLI cwd and new session","Typed session.create and project roots","Code: full","Code: full","Code: full","Needs packaged proof","Existing typed command","Do not create a second project database" +"P03","T1","Projects","Reveal project in native file manager","CLI/path known","Privacy-safe project reveal support in integration","Code: full","Platform","Code: partial","Mobile needs a native equivalent such as copy/share path","Platform adapter","Not every desktop action has a meaningful phone equivalent" +"P04","T1","Projects","Project file tree","OMP file tools and cwd","Bounded files.list authority","Code: full","Code: partial","Code: full","Responsive/mobile ergonomics need proof","Client UI over existing files.list","File scope stays confined by OMP authority" +"P05","T1","Projects","Search project files","grep/glob/search tools","Host file authority exists","Code: full","Code: full","Missing","PR #106 merged the confined host and web Quick Open path; Flutter has no equivalent screen","Add Flutter compact/wide Quick Open over the same files.search operation","Host confinement and result bounds are already implemented" +"P06","T1","Projects","Git branch and worktree context","Native git and worktree commands","T4 wire has optional workspace/runtime metadata","Code: partial","Code: partial","Code: partial","Context is not consistently visible","Project a read-only summary; keep mutation typed and guarded","Workspace features are negotiated, not universal" +"P07","T2","Projects","Create/import/archive/recover Git workspaces","OMP worktree management","T4 host wire defines lifecycle","Code: partial","Code: partial","Code: partial","Protocol breadth exceeds proven client UX","Typed workspace actions gated by negotiated feature","Do not infer support from command definitions alone" +"P08","T2","Projects","Project scripts and common commands","Shell and custom commands","No complete app-specific catalog proven","Missing","Missing","Missing","No ergonomic script surface","Read-only command catalog, then guarded bash.run","Avoid executing arbitrary catalog text without confirmation" +"S01","T0","Sessions","List filter sort group and search sessions","Resume picker and session metadata","Discovery paging and transcript index","Code: full","Code: full","Code: full","Needs live scale proof","Existing projection","Search is separate from project file search" +"S02","T0","Sessions","Create attach and resume sessions","Full native behavior","Typed lifecycle and attach","Code: full","Code: full","Code: full","No major source gap","Existing typed commands","Verify lock and observer transitions" +"S03","T0","Sessions","Rename session","/rename and RPC set_session_name","Typed session.rename","Code: full","Code: full","Code: full","No major source gap","Existing typed command","OMP remains title authority" +"S04","T0","Sessions","Archive and restore","Session management operations","Authority methods and typed commands","Code: full","Code: full","Code: full","No major source gap","Existing typed commands","Verify archive filters on each client" +"S05","T0","Sessions","Close terminate and delete","Native lifecycle","Typed close/delete and authority methods","Code: full","Code: full","Code: full","Destructive copy and confirmation need platform proof","Existing typed commands with confirmation","Delete must remain explicit" +"S06","T0","Sessions","Retry last turn","/retry and RPC retry","Typed session.retry plus verified slash handler","Code: full","Code: full","Code: full","No major source gap","Prefer typed command","Do not rely on slash text where typed command exists" +"S07","T1","Sessions","Pause and resume an active agent","/pause and runtime controls","Typed wire commands exist","Code: partial","Code: partial","Code: partial","Controls are not consistently visible and verified","Client UI over existing session.pause/resume","Distinguish pause from cancel" +"S08","T1","Sessions","Reset provider-facing state without replacing session","/fresh","Verified headless slash handler","Slash only","Slash only","Slash only","No first-class explanation or result","Typed RPC wrapper or keep an explicitly marked headless slash","Useful for provider conversation state" +"S09","T0","Sessions","Branch fork and navigate session tree","/branch /fork /tree and RPC branch/get_branch_messages","TUI commands are cataloged but not headless","Missing","Missing","Missing","Central OMP session-tree behavior is absent","Typed wire commands over existing RPC","Do not parse JSONL directly in clients" +"S10","T0","Sessions","Compact handoff and resume elsewhere","/compact /handoff /resume and RPC compact/handoff","Compact is typed; handoff/resume commands are TUI-only","Code: partial","Code: partial","Code: partial","Compaction exists; full handoff workflow does not","Typed wire commands over existing RPC","Show summary outcome and resulting session identity" +"S11","T2","Sessions","Move or re-root a session","/move has a headless handler","Verified headless slash handler","Slash only","Slash only","Slash only","Server-side path entry is awkward on mobile","Typed authority action with path validation","Keep root confinement in OMP" +"S12","T1","Sessions","Manual compaction and strategy visibility","Multiple compaction strategies and /compact","Typed compact command and state fields","Code: partial","Code: partial","Code: partial","Action exists but strategy/result UX is incomplete","Client UI over typed command plus projected result","Auto-compaction state should remain read-only truth" +"S13","T2","Sessions","Shake or reduce stale context","/shake has a headless handler","Verified headless slash handler","Slash only","Slash only","Slash only","No native explanation or result","Keep marked slash initially; later typed operation","Less common than ordinary compact" +"S14","T2","Sessions","Export dump and copy transcript","/export /dump /copy","Export and dump have headless handlers; copy is TUI-only","Slash only","Slash only","Slash only","Server-side output destinations are not ergonomic on devices","Typed artifact export and native share sheet","Do not assume a remote host clipboard is the user's clipboard" +"S15","T2","Sessions","Encrypted share collaboration join and leave","/share /collab /join /leave and browser guest","Share is headless; collaboration commands are TUI-only","Code: partial","Code: partial","Missing","Full read/write collaboration is not mapped","Typed collaboration contract with explicit permissions","Higher security and identity risk than transcript export" +"S16","T1","Sessions","Usage context and statistics","/usage /context /stats and CLI dashboards","Usage authority plus headless commands","Code: full","Code: full","Code: full","Detailed local stats may be thinner than OMP TUI","Keep structured usage; add read-only stats projection if valuable","Quota/reset data is provider-sensitive but not secret" +"S17","T3","Sessions","Changelog","/changelog","Verified headless slash handler","Slash only","Slash only","Slash only","No native release-notes surface","Link to T4 and OMP release notes with version pairing","Low frequency" +"C01","T0","Composer","Send prompt and stream response","Core OMP loop","Per-session RPC child plus typed prompt","Code: full","Code: full","Code: full","No major source gap","Existing typed command","Verify long streams and reconnect" +"C02","T0","Composer","Steer during a turn and send after-turn follow-up","Native steering and follow-up queues","Typed steer/followUp with modes","Code: full","Code: full","Code: full","No major source gap","Existing typed commands","UI must clearly show immediate versus after-turn delivery" +"C03","T1","Composer","Inspect and change queue modes","/queue and configurable steering/follow-up modes","State carries queue data; no complete first-class controls","Code: partial","Code: partial","Code: partial","Queue behavior is powerful but opaque","Typed queue-mode commands over existing RPC","Avoid encoding queue changes as prompt text" +"C04","T0","Composer","Truthful slash command catalog and execution","Official OMP exposes bounded get_available_commands","Released fallback has a broader static command catalog","Sprint: implemented; TS verified","Sprint: implemented; TS verified","Sprint: implemented; Flutter verification pending","Merged host contract, rejection, and desktop preservation are now propagated into both client families; packaged proof remains","Merge this sprint, then verify Flutter desktop/Android/iOS and web/Electron packages","PRs #111, #113, and #117 provide truth and preservation; this branch presents result.operations" +"C05","T0","Composer","Prompt images","Native image input","Typed prompt image and blob path","Code: full","Code: full","Code: full","Needs platform picker and upload proof","Existing typed command plus platform file picker","Keep blob bounds and MIME checks" +"C06","T1","Composer","File path and resource references","Autocomplete and internal URI schemes","Catalog and artifact support exist","Code: partial","Code: partial","Code: partial","Not all OMP resource schemes have native pickers","Client pickers backed by typed read-only catalogs","Do not expose private filesystem paths unnecessarily" +"C07","T0","Composer","Model thinking and fast mode","Native controls and role routing","Typed model/thinking/fast commands","Code: full","Code: full","Code: full","No major source gap","Existing typed commands","Model choices must come from live catalog" +"C08","T0","Composer","Plan and plan-review modes","/plan /plan-review and plan model role","Cataloged but TUI-only","Visible: terminal-only","Visible: terminal-only","Visible: terminal-only","The UI now explains that the command cannot run in-app, but there is still no typed mode control","Add typed mode operations through a stable official OMP seam; use a small extractable bridge method only if required","Do not simulate plan mode with a prompt prefix" +"C09","T0","Composer","Goal and guided-goal modes","/goal /guided-goal plus hidden goal tool","Cataloged but TUI-only","Visible: terminal-only","Visible: terminal-only","Visible: terminal-only","The UI now explains that the command cannot run in-app, but goal lifecycle is not mapped","Add typed goal commands and goal events through the official adapter","Goal state belongs to OMP" +"C10","T2","Composer","Loop and vibe workflows","/loop /vibe","Cataloged but TUI-only","Missing","Missing","Missing","Advanced modes have no app equivalent","Typed actions only after product semantics are clear; otherwise CLI handoff","Do not blindly copy terminal dialogs" +"C11","T1","Composer","Advisor and prewalk","/advisor /prewalk","Verified headless handlers","Slash only","Slash only","Slash only","Works without first-class progress/result UX","Keep marked slash initially; later typed action","Advisor has an independent context" +"C12","T2","Composer","Force a specific tool","/force","Verified headless handler","Slash only","Slash only","Slash only","Risky power control lacks structured validation","Typed tool selection using live catalog","Never accept an unknown tool name silently" +"C13","T2","Composer","Side question and tangent agent","/btw /tan","Cataloged but TUI-only","Missing","Missing","Missing","No native side-channel workflow","Typed ephemeral question and background-agent actions","Keep results visually separate from main transcript" +"C14","T2","Composer","Voice input and text-to-speech","Speech tools and CLI say/ttsr","Capacitor adds speech support; OMP tool remains runtime-owned","Code: partial","Code: partial","Code: partial","Coverage and provider behavior differ by platform","Platform speech adapter plus typed OMP speech artifact","Do not treat device dictation and OMP TTS as the same feature" +"C15","T2","Composer","Browser guest and browser mode","/browser and browser tool","Verified headless browser command plus T4 preview contract","Code: full","Code: partial","Code: partial","Mobile preview interactions need proof","Use T4 preview contract; keep OMP browser tool output as runtime truth","Authenticated previews require explicit authority" +"T01","T0","Transcript","Markdown code and thinking display","Rich TUI rendering","Structured entries","Code: full","Code: full","Code: full","No major source gap","Shared semantic entry model","Respect hidden-thinking policy" +"T02","T0","Transcript","Safe generic tool fallback","Tool events and structured results","Typed durable tool entries","Code: full","Code: full","Code: full","No major safety gap","Keep generic bounded JSON fallback","Unknown tools must remain visible rather than disappearing" +"T03","T1","Transcript","Specialized tool renderers","Rich terminal renderers","Tool metadata available","Code: partial: 24 of 29 built-in names specialized","Code: partial","Code: partial: generic structured cards","Five web built-ins and most Flutter tools lack specialized views","Share semantic tool-view models; render per platform","Generic fallback is safe but less readable" +"T04","T0","Transcript","Images artifacts and large outputs","Blob and artifact schemes","Typed images and artifact reads","Code: full","Code: full","Code: full","Needs packaged size/error proof","Existing typed artifact path","Never inline unbounded outputs" +"T05","T0","Transcript","Approvals questions and proposed-plan responses","Ask tool and approval modes","Attention Inbox and ui.respond","Code: full","Code: full","Code: full","Needs live cross-device race tests","Existing typed response with leases/revisions","OMP remains decision/outcome authority" +"T06","T0","Transcript","Errors retry compaction and interruption states","Native events","Projected session state and outcomes","Code: full","Code: full","Code: full","Needs real failure-matrix proof","Existing events and commands","Do not infer success from connection health" +"T07","T1","Transcript","Collaboration and system messages","Native events and shared sessions","Partial event projection","Code: partial","Code: partial","Code: partial","Some OMP-native event types use generic output","Extend durable entry union only for stable semantic events","Avoid copying presentation-only terminal events" +"A01","T0","Agents","Agent tree and lifecycle","/agents /tree and task/hub tools","Agent lifecycle feature and projected state","Code: full","Code: full","Code: full","No major source gap","Existing projection","Verify deeply nested trees" +"A02","T1","Agents","Progress and current activity","Agent/tool progress events","Agent progress feature","Code: full","Code: full","Code: full","Needs slow-task and reconnect proof","Existing event projection","Progress is not completion proof" +"A03","T1","Agents","Child transcript","agent:// resources and session children","Agent transcript feature","Code: full","Code: full","Code: full","No major source gap","Existing typed reads","Keep child identity stable" +"A04","T0","Agents","Cancel child agent","Task/hub control","Typed agent.cancel","Code: full","Code: full","Code: full","No major source gap","Existing typed command","Cancellation result must be explicit" +"A05","T1","Agents","Steer wake or message child agent","Hub/task coordination","No complete client action mapped","Missing","Missing","Missing","Viewing and cancellation are not full orchestration","Typed agent commands backed by OMP hub/task APIs","Do not inject text into child session files" +"A06","T1","Agents","Agent worktree and branch context","Optional isolated worktrees","Workspace metadata is optional","Code: partial","Code: partial","Code: partial","Ownership and branch context are thin","Read-only projection plus typed guarded mutations","Keep Git authority in runtime/workspace layer" +"A07","T2","Agents","Jobs and hub dashboard","/jobs and task/hub tools","Jobs command has headless handler; hub is tool-driven","Slash only","Slash only","Slash only","No first-class queue/dashboard","Read-only job projection, then bounded controls","Background process state should survive reconnect" +"A08","T1","Agents","Todos","/todo and todo tool","Verified headless command plus projected state","Code: partial","Code: partial","Code: partial","Readability/edit controls vary by client","Typed todo projection/actions","OMP remains todo authority" +"A09","T2","Agents","Agent inventory and role configuration","CLI agents and role settings","Catalog/settings metadata","Read only","Read only","Read only","No ergonomic management UI","Schema-driven settings plus bounded catalog","Avoid hand-maintained agent lists" +"D01","T0","Developer","List and read files","read/glob/find/search tools","Confined files.list/read authority","Code: full","Code: partial","Code: full","Mobile layout and large-file proof remain","Existing typed commands","Respect project-root confinement" +"D02","T0","Developer","Write and patch files","write/edit/ast tools","Revision-checked files.write/patch","Code: full","Code: partial","Code: full","Mobile confirmation and conflict UX need proof","Existing typed commands","Never bypass revision checks" +"D03","T0","Developer","Diff and turn review","Git/review custom commands and patches","files.diff and review.read/apply","Code: full","Code: partial","Code: full","Mobile review ergonomics need proof","Existing typed commands","Review applies immutable turn patches" +"D04","T1","Developer","Persistent terminal","PTY shell and jobs","Bounded term.open/input/resize/close","Code: full","Code: partial","Code: full","Phone keyboard/resizing and lifecycle need proof","Existing typed terminal commands","Terminal is an advanced mobile surface, not a copied desktop layout" +"D05","T1","Developer","One-shot bash","bash tool and CLI shell","Bounded bash.run","Code: full","Code: partial","Code: full","Confirmation and output bounds need proof","Existing typed command","Execution permission is separate from read permission" +"D06","T1","Developer","Browser preview navigate and capture","Browser tool and attached Electron","Broad T4 preview contract","Code: full","Code: partial","Code: full","Mobile remote preview proof remains","Existing typed preview commands","Use negotiated preview capabilities" +"D07","T2","Developer","Browser preview input upload and handoff","Browser tool interactions","Preview click/fill/scroll/type/key/select/upload/handoff vocabulary","Code: partial","Code: partial","Code: partial","Protocol breadth is not full UI proof","Finish UI only for advertised live capabilities","Authenticated-profile use requires explicit opt-in" +"D08","T2","Developer","Audit and raw event tail","Debug and event hooks","audit.read and audit tail","Code: full","Code: partial","Code: full","Mobile filters/export may be thin","Read-only diagnostic UI","Redact secrets before transport" +"D09","T1","Developer","Logs diagnostics and health","/debug provider health and CLI operations","Broker/runtime diagnostics and redaction","Code: partial","Code: partial","Code: partial","No single end-to-end support bundle","T4-owned diagnostics bundle with OMP version/protocol IDs","Never include credentials or raw tokens" +"M01","T0","Models and auth","Model/provider inventory and model switch","Broad provider/model catalog","Schema-backed catalog and typed model selection","Code: full","Code: full","Code: full","No major source gap","Existing catalog and command","Live catalog labels are authoritative" +"M02","T1","Models and auth","Role routing for plan smol slow vision advisor and others","Native role settings","Catalog/settings expose roles","Code: partial","Code: partial","Code: partial","Full role mapping is hard to understand in clients","Schema-driven role editor with plain-English descriptions","Do not hard-code provider lists" +"M03","T1","Models and auth","Provider health and credential presence","Native auth and usage status","Broker status with secret redaction","Code: full","Code: full","Code: full","Needs provider-specific error proof","Existing broker status","Show presence and health, never credential values" +"M04","T0","Models and auth","Provider login logout and setup","/login /logout /setup and RPC get_login_providers/login","Slash commands are TUI-only; broker is mostly status","Missing","Missing","Missing","Core account setup still requires terminal","Typed auth flow over existing RPC; keep secrets on host","OAuth and MFA may require a browser or CLI handoff" +"M05","T1","Models and auth","Custom provider and models.yml configuration","Native config","config.write and schema settings","Code: partial","Code: partial","Code: partial","Advanced validation/editor is incomplete","Schema-backed structured editor with raw-file escape hatch","Never return stored secrets" +"M06","T1","Models and auth","Quota usage and reset times","/usage and provider adapters","Structured usage.read","Code: full","Code: full","Code: full","No major source gap","Existing typed read","Separate unknown usage from zero usage" +"X01","T1","Extensibility","Tool inventory and availability","/tools and tool registry","Catalog metadata","Read only","Read only","Read only","Inventory exists; enable/disable UX is incomplete","Catalog plus schema-driven settings","Tool policies belong to OMP" +"X02","T2","Extensibility","MCP servers resources prompts OAuth and reconnect","/mcp and MCP config","Verified headless command; catalog/settings foundations","Slash only","Slash only","Slash only","Management still depends on command text or terminal","Typed bounded MCP management; begin read-only","OAuth may require external browser handoff" +"X03","T2","Extensibility","Extensions inventory enable disable and status","/extensions and extension discovery","Command is TUI-only; catalog/settings partial","Read only","Read only","Read only","No complete lifecycle UI","Read-only inventory then typed enable/disable/reload","Extensions can execute code; preserve clear trust warnings" +"X04","T2","Extensibility","Marketplace and plugins install update enable disable","/marketplace /plugins /reload-plugins and omp plugin","Headless handlers exist for marketplace/plugins/reload","Slash only","Slash only","Slash only","No safe progress permission or restart UX","Typed package operations with source and confirmation","Installation changes executable code" +"X05","T2","Extensibility","Skills inventory selection and skill commands","Skills and /skill:","Catalog/settings can describe skills","Read only","Read only","Read only","Dynamic skill commands are not fully classified","Generated manifest plus read-only detail; typed selection","Skill resources should be fetched through stable IDs" +"X06","T2","Extensibility","Custom and bundled commands","Bundled /review and /green plus custom commands","Catalog partially exposes commands","Code: partial","Code: partial","Code: partial","Official OMP discovery classifies currently available headless commands, but dynamic lifecycle and richer metadata still need proof","Use the shared operation contract for every discovered command and refresh after extension changes","Never assume an undiscovered custom command is headless-safe" +"X07","T3","Extensibility","Hooks custom renderers keybindings and providers","Rich extension API and TUI components","Only stable runtime events/catalog data should cross","Platform","Platform","Platform","Terminal UI plugins cannot run unchanged in native clients","Define small semantic extension points in T4; use CLI handoff for terminal UI","Do not make T4 execute untrusted terminal renderers" +"X08","T2","Extensibility","Reload extensions plugins and MCP","Native reload paths","Some verified headless handlers","Slash only","Slash only","Slash only","No unified result/restart model","Typed reload actions with returned status","Reload can interrupt active behavior" +"K01","T1","Context and memory","Context meter report and compaction state","/context and native status","Projected context and compaction fields","Code: full","Code: full","Code: full","Detailed strategy explanation may be thin","Read-only projection","Use OMP token accounting" +"K02","T1","Context and memory","Applied project rules skills and context disclosure","/context and discovery","Discovery/catalog supports partial detail","Code: partial","Code: partial","Code: partial","Users cannot always see why context was included","Read-only structured context report","Do not duplicate rule discovery in clients" +"K03","T2","Context and memory","Retain recall reflect and memory search","Memory tools and /memory","Tool events plus generic fallback","Code: partial","Code: partial","Code: partial","Tools can run but there is no first-class memory browser","Typed read-only memory view; keep writes as OMP tools initially","Memory backends differ" +"K04","T2","Context and memory","Memory maintenance edit learn and manage skill","memory_edit learn manage_skill tools","Generic tool fallback only","Code: partial","Code: partial","Code: partial","No specialized renderers or management UI","Shared semantic tool views then bounded management actions","These are three of the five unspecialized web built-ins" +"K05","T3","Context and memory","OMFG TTSR and deep maintenance workflows","/omfg and CLI ttsr","TUI/CLI-specific","Missing","Missing","Missing","Niche workflows have no app path","Explicit CLI handoff unless demand justifies typed contract","Keep out of Tier 0 parity claims" +"K06","T2","Context and memory","Checkpoint and rewind","checkpoint and rewind tools","Generic tool fallback only","Code: partial","Code: partial","Code: partial","No first-class history control or specialized result","Typed guarded actions plus visual history explanation","Potentially destructive to conversation state" +"G01","T0","Settings","Read full settings schema and effective values","415 top-level settings; 303 with UI metadata","Schema-backed secret-safe catalog/settings authority","Code: full","Code: full","Code: full","No major foundation gap","Continue generated UI","Do not manually duplicate hundreds of settings" +"G02","T1","Settings","Write scoped settings reset and source visibility","Layered global/project/CLI/runtime config","Typed settings.write and config.write","Code: full","Code: full","Code: full","Reset/scope explanation needs platform proof","Existing typed commands with source metadata","Show which layer wins" +"G03","T0","Settings","Secret-safe provider setup","Native login and masked settings","Broker hides secrets; auth action missing","Code: partial","Code: partial","Code: partial","Safe reads exist but login/setup does not","Typed auth flow that keeps secrets on host","Never send raw stored credentials to clients" +"G04","T1","Settings","Platform applicability and restart-required state","Settings metadata and runtime behavior","Catalog has type/source/scope/platform metadata","Code: partial","Code: partial","Code: partial","Restart/applicability feedback is incomplete","Extend catalog metadata if original schema can supply it","Disable controls not supported by the active platform" +"G05","T2","Settings","Themes contrast and accessibility","TUI themes and accessibility settings","Clients own native presentation","Code: full","Code: partial","Code: full","Cross-platform parity needs visual/accessibility proof","Platform UI settings; map only semantic OMP options","Do not copy terminal color mechanics literally" +"G06","T3","Settings","Hotkeys and keybinding reference","/hotkeys and extension keybindings","TUI-only command","Platform","Platform","Platform","Native clients need their own shortcut help","Client-owned shortcut catalog","Functional equivalence rather than key-for-key parity" +"G07","T1","Settings","Notifications and attention alerts","TUI notifications and events","Attention projection exists","Code: partial","Code: partial","Code: partial","Native notification permission and deep links need proof","Platform notification adapters over attention events","Avoid leaking prompt content on lock screens by default" +"G08","T1","Settings","Application and runtime updates","OMP update command; provider/package updates","T4 release lifecycle is separate","Code: partial","Code: full for Capacitor updater","Code: partial","Flutter/Desktop cutover and paired-runtime updates are incomplete","T4-owned updater with compatibility matrix","Never update one side of the pinned pair blindly" +"G09","T1","Settings","Diagnostics and support bundle","Debug logs stats changelog","T4 audit broker and runtime identity","Code: partial","Code: partial","Code: partial","No one-click redacted proof bundle","T4-owned support artifact with exact version pins","Include what was verified and what could not be checked" diff --git a/provenance/t3code/imports/f2-transcript-20260711.json b/provenance/t3code/imports/f2-transcript-20260711.json index 4309f0d9..ce38b6fb 100644 --- a/provenance/t3code/imports/f2-transcript-20260711.json +++ b/provenance/t3code/imports/f2-transcript-20260711.json @@ -30,7 +30,7 @@ "sourceBlobSha": "b2fb2e223d3b7b85f993b48558222f708c4c6b54;c4919b1924515d1611c33a01bc6029f50605c749", "targetPath": "apps/web/src/features/composer/slash.ts + apps/web/src/features/composer/match.ts", "classification": "adapted", - "checksum": "sha256:833f8ebd576e4e37db527b8a5dac4c8dfbe31d85295b796c1ea26b3bbcd4acc5;sha256:de8963354251eba7321b17b62e9b6de1aa550b5e3ad6be5f2bc9c9635697aefc" + "checksum": "sha256:b3edcf2f93240be8d96a3c28183776a34b60a54350253e4974d66de32d4018e6;sha256:de8963354251eba7321b17b62e9b6de1aa550b5e3ad6be5f2bc9c9635697aefc" }, { "sourcePath": "apps/web/src/components/chat/ContextWindowMeter.tsx",