@omercnet/paseo-omp 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +21 -0
  3. package/README.md +110 -0
  4. package/SUPPORT.md +40 -0
  5. package/TESTING.md +147 -0
  6. package/client/hub-icon.tsx +12 -0
  7. package/client/hub-popover.tsx +132 -0
  8. package/client/hub-status.ts +29 -0
  9. package/client/memory-panel.tsx +71 -0
  10. package/client/memory-popover.tsx +70 -0
  11. package/client/omp-config-surface.tsx +1274 -0
  12. package/client/omp-doc-links.ts +117 -0
  13. package/client/omp-plugin-manager.tsx +833 -0
  14. package/client/provider-diagnostics-state.ts +250 -0
  15. package/client/provider-icon.tsx +27 -0
  16. package/client/provider-image.tsx +66 -0
  17. package/client/quota-popover.tsx +150 -0
  18. package/client/quota-state.ts +131 -0
  19. package/client/sessions-popover.tsx +73 -0
  20. package/docs/alpha-release-checklist.md +70 -0
  21. package/docs/configuration.md +122 -0
  22. package/docs/core-provider-issue-audit.md +108 -0
  23. package/docs/installation.md +73 -0
  24. package/index.client.tsx +272 -0
  25. package/index.server.ts +51 -0
  26. package/package.json +84 -0
  27. package/paseo-plugin.json +5 -0
  28. package/server/hub.ts +145 -0
  29. package/server/memory.ts +86 -0
  30. package/server/mutation-queue.ts +12 -0
  31. package/server/omp-config.ts +126 -0
  32. package/server/omp-plugins.ts +627 -0
  33. package/server/omp-settings.ts +291 -0
  34. package/server/paths.ts +64 -0
  35. package/server/provider/catalog.ts +173 -0
  36. package/server/provider/config-normalization.ts +148 -0
  37. package/server/provider/connection.ts +992 -0
  38. package/server/provider/host-tools.ts +706 -0
  39. package/server/provider/image.ts +143 -0
  40. package/server/provider/mcp-transport.ts +394 -0
  41. package/server/provider/omp-rpc.ts +2739 -0
  42. package/server/provider/omp.svg +5 -0
  43. package/server/provider/provider-options.ts +27 -0
  44. package/server/provider/registration.ts +151 -0
  45. package/server/provider/security.ts +317 -0
  46. package/server/provider/session-descriptors.ts +431 -0
  47. package/server/provider/session.ts +4451 -0
  48. package/server/provider/settings.ts +78 -0
  49. package/server/provider/subsessions.ts +847 -0
  50. package/server/provider/timeline-projector.ts +1764 -0
  51. package/server/provider-diagnostics.ts +1057 -0
  52. package/server/quota.ts +54 -0
  53. package/server/sessions.ts +58 -0
  54. package/shared/hub.ts +43 -0
  55. package/shared/memory.ts +23 -0
  56. package/shared/omp-config.ts +81 -0
  57. package/shared/omp-plugins.ts +223 -0
  58. package/shared/omp-settings.ts +207 -0
  59. package/shared/provider-diagnostics.ts +117 -0
  60. package/shared/provider-image.ts +160 -0
  61. package/shared/quota.ts +22 -0
  62. package/shared/sessions.ts +23 -0
  63. package/tsconfig.json +16 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,87 @@
1
+ # Changelog
2
+
3
+ ## [0.2.1](https://github.com/omercnet/paseo-plugins/compare/paseo-omp-v0.2.0...paseo-omp-v0.2.1) (2026-09-15)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **paseo-omp:** handle oversized pasted images ([#55](https://github.com/omercnet/paseo-plugins/issues/55)) ([4b6ed7d](https://github.com/omercnet/paseo-plugins/commit/4b6ed7d3015f1f85dcc13a9dd29061efef5f22c7))
9
+ * **paseo-omp:** preserve buffered terminal ownership evidence ([#54](https://github.com/omercnet/paseo-plugins/issues/54)) ([b49400e](https://github.com/omercnet/paseo-plugins/commit/b49400e09d73cd67a23986f52c17bfc1f23cd297))
10
+ * **paseo-omp:** steer auto messages during active turns ([#61](https://github.com/omercnet/paseo-plugins/issues/61)) ([b36002b](https://github.com/omercnet/paseo-plugins/commit/b36002bbc5b108cd525dd9c356fbdb89d6a9f3e6))
11
+
12
+ ## [0.2.0](https://github.com/omercnet/paseo-plugins/compare/paseo-omp-v0.1.2...paseo-omp-v0.2.0) (2026-09-14)
13
+
14
+
15
+ ### Features
16
+
17
+ * **paseo-omp:** add config workspace shell ([#48](https://github.com/omercnet/paseo-plugins/issues/48)) ([1c0126b](https://github.com/omercnet/paseo-plugins/commit/1c0126b51bd3c8ceccacf7758fc14270cd67440e))
18
+ * **paseo-omp:** add opt-in output redaction ([#44](https://github.com/omercnet/paseo-plugins/issues/44)) ([0b0cea1](https://github.com/omercnet/paseo-plugins/commit/0b0cea1501230437fe4d6390b67ca25ee2079c93))
19
+ * **paseo-omp:** browse complete runtime settings catalog ([#49](https://github.com/omercnet/paseo-plugins/issues/49)) ([ef68cd7](https://github.com/omercnet/paseo-plugins/commit/ef68cd754fa3aad4a15bcdc8649f8c7dcdcde79e))
20
+ * **paseo-omp:** edit scalar settings in sidebar ([#50](https://github.com/omercnet/paseo-plugins/issues/50)) ([78da43f](https://github.com/omercnet/paseo-plugins/commit/78da43fd155ed06e29faef507156399b55cf3439))
21
+ * **paseo-omp:** support explicit env passthrough ([#39](https://github.com/omercnet/paseo-plugins/issues/39)) ([e22b3b4](https://github.com/omercnet/paseo-plugins/commit/e22b3b448f218bdecfcb3a168c14fa32051a6392))
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * **omp:** preserve safe published URLs ([#42](https://github.com/omercnet/paseo-plugins/issues/42)) ([4530430](https://github.com/omercnet/paseo-plugins/commit/4530430a02bee076f8940c376401d3266ce17499))
27
+ * **paseo-omp:** align prompt attachment rendering ([#41](https://github.com/omercnet/paseo-plugins/issues/41)) ([4b708c8](https://github.com/omercnet/paseo-plugins/commit/4b708c897b4af4cd6673e7581f736e95843b545b))
28
+ * **paseo-omp:** expose native Paseo tools ([#52](https://github.com/omercnet/paseo-plugins/issues/52)) ([3b52adf](https://github.com/omercnet/paseo-plugins/commit/3b52adff1de6329fa9f0bc4a1dcea339166e3974))
29
+ * **paseo-omp:** label direct MCP tool calls ([#53](https://github.com/omercnet/paseo-plugins/issues/53)) ([e09b1fd](https://github.com/omercnet/paseo-plugins/commit/e09b1fd2c05cb45422e7d0633e8b638d0841b931))
30
+ * **paseo-omp:** preserve structured tool results ([#46](https://github.com/omercnet/paseo-plugins/issues/46)) ([9e5fe98](https://github.com/omercnet/paseo-plugins/commit/9e5fe989e57e100f67285257cada3242218eee58))
31
+ * **paseo-omp:** render WebP images across clients ([#51](https://github.com/omercnet/paseo-plugins/issues/51)) ([397cb2d](https://github.com/omercnet/paseo-plugins/commit/397cb2d1e660e43e22312ecf9f054d77afcddc6e))
32
+ * **paseo-omp:** stop speculative stream redaction ([#40](https://github.com/omercnet/paseo-plugins/issues/40)) ([539cb61](https://github.com/omercnet/paseo-plugins/commit/539cb61db0b904b8130319c7887852f991c50aba))
33
+
34
+ ## [0.1.2](https://github.com/omercnet/paseo-plugins/compare/paseo-omp-v0.1.1...paseo-omp-v0.1.2) (2026-09-13)
35
+
36
+
37
+ ### Bug Fixes
38
+
39
+ * **paseo-omp:** serialize persistent session registration ([#35](https://github.com/omercnet/paseo-plugins/issues/35)) ([03d38bc](https://github.com/omercnet/paseo-plugins/commit/03d38bc7e4a3d00ec54190e2b234a459bbdc55ae))
40
+ * **paseo-omp:** settle late child dispatches ([#34](https://github.com/omercnet/paseo-plugins/issues/34)) ([0746f11](https://github.com/omercnet/paseo-plugins/commit/0746f11fd94f15028fd624ac7a99ac8ebd88b63d))
41
+
42
+ ## [0.1.1](https://github.com/omercnet/paseo-plugins/compare/paseo-omp-v0.1.0...paseo-omp-v0.1.1) (2026-09-13)
43
+
44
+
45
+ ### Bug Fixes
46
+
47
+ * **paseo-omp:** preserve Plexus plugin credentials ([#27](https://github.com/omercnet/paseo-plugins/issues/27)) ([0674ef1](https://github.com/omercnet/paseo-plugins/commit/0674ef1d3d66efa37ec9d2da9a19dd57472c2cdd))
48
+ * **paseo-omp:** wait for session registration ([#30](https://github.com/omercnet/paseo-plugins/issues/30)) ([82e901e](https://github.com/omercnet/paseo-plugins/commit/82e901e4bb0e66cafeb572c4bd92e0461b57a647))
49
+
50
+ ## 0.1.0 (2026-09-13)
51
+
52
+
53
+ ### Features
54
+
55
+ * **paseo-omp:** add first OMP provider plugin ([#19](https://github.com/omercnet/paseo-plugins/issues/19)) ([f6f3e5e](https://github.com/omercnet/paseo-plugins/commit/f6f3e5e224bf8537f85305d79ea5d1bcf5849549))
56
+
57
+ ## Changelog
58
+
59
+ ## Unreleased
60
+
61
+ ### Features
62
+
63
+ * Add the permanent `omp-plugin` direct provider, OMP workspace tools, diagnostics, versioned persistence, and nested-subagent support.
64
+ * Add Release Please packaging, Git installation, compatibility policy, and release rollback guidance.
65
+ * Add a digest-pinned official Paseo Docker canary with deterministic mock, configured MCP, and optional local Ollama inference.
66
+ * Document the deduplicated Paseo core OMP issue audit, verified plugin fixes, host-owned concerns, and remaining alpha gaps.
67
+ * Add the OMP sidebar configuration editor with revision-checked scalar writes, structured collection display, and documentation of provider-profile options including names-only inherited environment settings.
68
+ * Add official OMP documentation links and an OMP-native plugin manager with status, user-scoped lifecycle actions, upgrades, write-only non-secret scalar configuration, and presence-only secret handling.
69
+
70
+ ### Bug Fixes
71
+
72
+ * Cancel pending generic permissions before interrupting OMP so the host receives a terminal cancellation.
73
+ * Rebuild the native branch watermark during replay so a post-rewind prompt retains terminal ownership.
74
+ * Label `xd://` and MCP timeline calls with their registered human-readable title instead of the generic `write` transport or technical route name.
75
+ * Support host-wide session discovery when Paseo opens the unscoped import sheet, preventing its provider failure path from crashing affected 0.8 daemons.
76
+ * Recover degraded or compacted `agent_end` outcomes from complete streamed assistant evidence while continuing to fail closed on partial evidence.
77
+ * Preserve `PLEXUS_API_KEY` when launching OMP so Plexus extension providers can attach their configured bearer header.
78
+ * Serialize concurrent persistent-session registration so startup restoration waits instead of returning transient provider failures.
79
+ * Expose caller-scoped Paseo orchestration tools under their native names so OMP skills can invoke `list_profiles`, `create_agent`, and related tools without CLI fallback.
80
+ * Render validated WebP timeline images on capable clients, show a local fallback when decoding fails, hide machine-facing coordinate annotations, and request PNG/JPEG output from OMP for cross-client compatibility.
81
+
82
+ ### Security
83
+
84
+ * Split release metadata from least-privilege artifact publishing, gate publication on exact-commit CI, and attest release artifacts.
85
+ * Make release archives self-contained, restrict source inputs to tracked allowlisted files, and verify offline installation plus contained extraction.
86
+ * Disable dependency lifecycle scripts during Git installation and verify both plugin entries from a fresh checkout.
87
+ * Document native-output fidelity, optional best-effort configured-value replacement, its limits, and the host-owned redaction boundary.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Omer Cohen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # Paseo OMP plugin
2
+
3
+ Community OMP integration for Paseo. The plugin registers the distinct `omp-plugin` provider and coexists with Paseo's bundled `omp` provider.
4
+
5
+ > **Alpha preview:** persistence and protocol contracts are tested, but upgrades may still require re-importing sessions created by an earlier preview.
6
+
7
+ ## Quick start
8
+
9
+ Requirements: Paseo `^0.8.0`, OMP `18.1.15` or newer, and OMP RPC protocol v2.
10
+
11
+ ```bash
12
+ paseo plugin add omercnet/paseo-plugins:paseo-omp --ref paseo-omp-v<version>
13
+ paseo plugin ls paseo-omp
14
+ ```
15
+
16
+ Open the **OMP** sidebar to review the provider-profile contract, browse and edit native scalar settings, manage OMP-native plugins, verify runtime health, and inspect storage and process diagnostics. Then create an agent and select **OMP Plugin**. Normal use requires no plugin-specific settings.
17
+
18
+ - [Install, update, rollback, and local development](docs/installation.md)
19
+ - [Configuration and every provider option](docs/configuration.md)
20
+ - [Compatibility, limitations, and support](SUPPORT.md)
21
+ - [Test matrix and release verification](TESTING.md)
22
+ - [Core-provider issue and parity audit](docs/core-provider-issue-audit.md)
23
+ - [Alpha release checklist](docs/alpha-release-checklist.md)
24
+
25
+ The plugin uses only public Paseo 0.8 provider contracts and registers the distinct `omp-plugin` identity; it does not modify the bundled `omp` provider.
26
+
27
+ ## Paseo provider SDK coverage
28
+
29
+ This matrix inventories the complete capability set exported by the pinned `@getpaseo/plugin` 0.8.0 provider SDK. It measures strict SDK surface coverage, not general product quality.
30
+
31
+ **Current capability completeness: 55.9%.** The provider advertises 10 of 17 capabilities (58.8%); nine are complete and `session.configure` is partial.
32
+
33
+ Scoring is deliberately mechanical so releases remain comparable:
34
+
35
+ - **100%**: advertised, implemented end to end, and covered by automated provider tests.
36
+ - **50%**: advertised with a meaningful subset, but at least one material SDK operation is rejected.
37
+ - **0%**: not advertised; requests are rejected rather than silently approximated.
38
+ - Overall completeness is the unweighted arithmetic mean of all 17 SDK capability rows.
39
+
40
+ | SDK capability | Completeness | Support and gap |
41
+ | --- | ---: | --- |
42
+ | `prompt.message` | **100%** | Text, structured attachments, optimistic correlation, streaming, and terminal outcomes map to native OMP prompts. |
43
+ | `prompt.command` | **100%** | Native command discovery plus structured `compact`, `autocompact`, `handoff`, `steer`, and `follow-up` dispatch. Individual commands can still fail when their documented OMP prerequisites are absent. |
44
+ | `prompt.image` | **100%** | Native image blocks for capable models; private bounded temporary files with cleanup for text-only models. |
45
+ | `prompt.output_schema` | **0%** | Not advertised. OMP has no equivalent structured-output contract; `outputSchema` fails visibly. |
46
+ | `prompt.steer` | **100%** | Native steering with turn ownership, user-message correlation, stale-target rejection, and terminal race handling. |
47
+ | `session.archive` | **0%** | Not advertised. Paseo may archive its agent record, but OMP has no native transcript archive operation. |
48
+ | `session.configure` | **50%** | Live model and thinking changes commit and republish native state. Live approval-mode changes require a new session, and non-empty SDK `settings` are rejected. |
49
+ | `session.list` | **100%** | Bounded host-wide and cwd-scoped discovery, search, previews, profile-specific session roots, and cleanup-reservation guards. |
50
+ | `session.persistence` | **100%** | Versioned opaque handles, exact resume, import, replay-before-ready, runtime recovery, and cleanup quarantine. |
51
+ | `session.revert.both` | **0%** | Not advertised because OMP cannot atomically rewind conversation and workspace files. |
52
+ | `session.revert.conversation` | **100%** | Native branch-based conversation rewind, replay deduplication, ownership transfer, and failure quarantine. |
53
+ | `session.revert.files` | **0%** | Not advertised because OMP does not expose a native file-only rewind contract. |
54
+ | `session.subsession` | **100%** | Direct and nested OMP subagents become Paseo child sessions with lifecycle, ancestry, progress, and replay. |
55
+ | `session.unarchive` | **0%** | Not advertised; there is no native OMP archive state to reverse. |
56
+ | `permission` | **100%** | Typed tool permissions when negotiated, with a bounded generic interaction fallback for OMP 18.1.15. Allow, deny, cancel, timeout, and interruption are covered. |
57
+ | `permission.tool_policy` | **0%** | Not advertised. Exact MCP preapproval policy cannot be preserved through OMP `set_host_tools`, so the plugin fails closed instead of broadening access. |
58
+ | `timeline.plugin` | **0%** | Not advertised. The provider currently expresses all output with Paseo's built-in timeline item types and registers no provider-owned custom timeline renderer. |
59
+
60
+ Provider functionality outside the capability flags is tracked separately:
61
+
62
+ | Provider SDK surface | Completeness | Implementation |
63
+ | --- | ---: | --- |
64
+ | Registration metadata and sanitized SVG icon | **100%** | Stable `omp-plugin` identity, label, description, and bundled icon. |
65
+ | Strict `providerOptionsSchema` | **100%** | Command, literal and names-only inherited environment, output-redaction mode, session directory, RPC timeout, and role-model options are validated and normalized. |
66
+ | Availability diagnostics | **100%** | Bounded checks distinguish missing, unrunnable, incompatible, and available OMP runtimes. |
67
+ | Catalog cache identity | **100%** | Hash includes effective options, configured inherited-environment names, settings, scope, cwd, and default command, but never resolves or fingerprints inherited values. |
68
+ | Models, modes, and thinking catalog | **100%** | Native catalog is mapped to opaque public model IDs with committed defaults and permission-gated modes. |
69
+ | Connection `send` / `onEvent` / `close` lifecycle | **100%** | Request correlation, multi-session ownership, process recovery, teardown, and provider reload/removal are covered. |
70
+ | Session launch: cwd, env, system prompt, title, and persistence | **100%** | Complete launch configuration is bounded, validated, forwarded, and re-read on recovery; selected daemon environment values are resolved only when catalog and session children spawn. |
71
+ | MCP server forwarding and host tools | **100%** | Configured MCP tools are discovered, namespaced, labeled, executed, canceled, and bounded. Caller-scoped Paseo orchestration tools retain their native names, such as `create_agent` and `list_profiles`, so OMP skills can invoke them directly. Exact `toolPolicy` remains the separate 0% capability above. |
72
+ | Denied native tools | **100%** | `disallowedTools` becomes an explicit OMP allow-list; unknown names fail closed. |
73
+ | Commands and committed session state events | **100%** | Publishes `session.commands`, `session.opened`, `session.config`, `session.ready`, and request completion in protocol order. |
74
+ | Built-in timeline snapshots | **100%** | Assistant, reasoning, tools, todos, notifications, errors, compaction, and friendly MCP labels use stable IDs and complete snapshots. OMP emits client-safe PNG/JPEG when possible; validated legacy WebP images render on capable clients with an explicit per-image fallback elsewhere. |
75
+ | Usage reporting | **100%** | Periodic, post-compaction, fallback, terminal, timeout, and recovered-runtime samples publish `session.usage`. |
76
+
77
+ Tracking rules:
78
+
79
+ 1. On every `@getpaseo/plugin` upgrade, diff the SDK's exported `PROVIDER_CAPABILITIES` list and add every new capability here at **0%** before claiming support.
80
+ 2. Raise a row only when the provider advertises the capability and an observable contract test covers its success and failure boundaries.
81
+ 3. Keep native-agent limitations at less than 100% even when the adapter itself is complete; do not count undocumented fallbacks as support.
82
+ 4. Keep the detailed evidence and regression locations in [TESTING.md](TESTING.md); this README is the public progress ledger.
83
+
84
+ ## Compatibility and coexistence
85
+
86
+ The plugin always remains a separate provider. It registers only `omp-plugin`; it never registers, aliases, overrides, removes, or migrates Paseo's bundled `omp` provider. Both identities may be enabled on the same daemon and selected independently per agent.
87
+
88
+ Existing agents whose provider is `omp` remain owned by the bundled provider. New plugin agents persist under `omp-plugin` with the plugin's versioned opaque handle. The plugin can list and import OMP-native sessions through its own provider flow, but it performs no implicit conversion of bundled-provider records.
89
+
90
+ OMP `18.1.15` is the oldest version tested end to end. The direct provider's hard compatibility gate is `rpc-ui` protocol v2: metadata-free legacy ready frames and v1-only runtimes are rejected before a provider session opens because they cannot support the advertised persistence and conversation-rewind capabilities. Typed tool approvals remain capability-gated and fall back as described above.
91
+
92
+ Install, update, disable, or remove `paseo-omp` independently of the bundled provider. Verify the provider snapshot contains `omp-plugin` after installation; a bundled `omp` entry may remain present and is not modified by this plugin.
93
+
94
+ ## Output and credential boundary
95
+
96
+ The plugin validates OMP protocol data and bounds public strings and structured values. It does not heuristically redact or rewrite content produced by native OMP, models, or tools. Do not put credentials in prompts or tool output, because that content may be published unchanged after validation and bounding.
97
+
98
+ `providerOptions.outputRedaction` defaults to `none`, preserving native content subject to those bounds. `configured-values` provides best-effort literal replacement for explicitly supplied credential values and every non-empty daemon value selected through `inheritEnv`, regardless of its name. It does not detect generated secrets or encoded, transformed, or independently streamed fragments of configured values. Centralized Paseo policy is required when a deployment needs redaction guarantees.
99
+
100
+ Unexpected or internal launch failures use fixed fallback messages rather than serializing the launch configuration. Explicit public validation errors may include caller-supplied configuration names or values. Deployments that require host-wide content redaction should implement it in a dedicated host or plugin layer rather than this protocol adapter.
101
+
102
+ ## Testing and development
103
+
104
+ The repeatable Docker canary and every validation command are documented in [TESTING.md](TESTING.md). It covers discovery, modes, text, images, tools, MCP, permissions, steering, interruption, persistence, host-wide and scoped session listing, import, resume, subagents, Hub, usage, plugin RPCs, and conversation rewind.
105
+
106
+ Use `canary-mock/Deterministic Canary` for assertions. The optional local Ollama model is exploratory and nondeterministic.
107
+
108
+ ## Support
109
+
110
+ See [SUPPORT.md](SUPPORT.md) for ownership, escalation boundaries, supported versions, security reporting, and the repeatable OMP RPC compatibility intake process.
package/SUPPORT.md ADDED
@@ -0,0 +1,40 @@
1
+ # Support
2
+
3
+ ## Ownership
4
+
5
+ `@omercnet` maintains the `paseo-omp` package, its release artifacts, and the translation between Paseo's provider protocol and OMP's `rpc-ui` protocol. Support is best effort; no response-time or compatibility SLA is promised.
6
+
7
+ Report plugin packaging, installation, provider behavior, and compatibility failures in the [paseo-plugins issue tracker](https://github.com/omercnet/paseo-plugins/issues). Use the [OMP RPC compatibility template](https://github.com/omercnet/paseo-plugins/issues/new?template=omp-rpc-compatibility.yml) for native protocol changes.
8
+
9
+ After the failure is isolated:
10
+
11
+ - report an OMP CLI or `rpc-ui` implementation defect to [Oh My Pi](https://github.com/can1357/oh-my-pi/issues);
12
+ - report a Paseo plugin SDK, loader, or provider-protocol defect to [Paseo](https://github.com/getpaseo/paseo/issues);
13
+ - keep adaptation, packaging, and cross-project compatibility work in this repository.
14
+
15
+ Do not put credentials, private repository paths, session transcripts, or unredacted RPC payloads in an issue. Report vulnerabilities through the [private GitHub Security Advisory form](https://github.com/omercnet/paseo-plugins/security/advisories/new), not a public issue. Release checksums detect accidental corruption only; authenticate downloaded archives with their signed GitHub build-provenance attestation.
16
+
17
+ ## Supported versions
18
+
19
+ - Paseo: `^0.8.0` on both the daemon and every app loading the client entry.
20
+ - OMP: `18.1.15` is the oldest release in the required real-binary regression job. The hard runtime contract is `rpc-ui` protocol v2, not the version string alone.
21
+ - Plugin release channel: alpha. Backward compatibility is best effort until stable `0.1.0`; every known migration requirement must be stated in the release notes.
22
+ - Typed approvals: optional. When both peers negotiate `typedToolApprovals: 1`, the plugin uses typed tool permissions. Otherwise it retains the bounded generic extension-question flow.
23
+
24
+ ## Known limitations
25
+
26
+ - `omp` and `omp-plugin` are independent provider identities. Agents, provider settings, and persisted handles do not migrate automatically between them.
27
+ - Do not open the same underlying OMP session concurrently through both providers. Reservation tracking is provider-local and cannot coordinate ownership with Paseo's bundled adapter.
28
+ - OMP 18.1.15 does not advertise typed tool approvals. The plugin uses its bounded generic permission fallback until both peers negotiate `typedToolApprovals: 1`.
29
+ - Configured MCP servers are supported and bridged into OMP. Paseo's own orchestration tools appear under their native names when the daemon's **Enable Paseo tools** / `daemon.mcp.injectIntoAgents` setting is enabled; other MCP servers remain namespaced. Exact Paseo `toolPolicy` preapproval cannot be represented by OMP `set_host_tools` and therefore fails session startup closed. `disallowedTools` applies only to recognized native OMP built-ins; unknown names are rejected and MCP tools are not silently filtered through it.
30
+ - `qwen2.5:0.5b` is provided only for free exploratory inference. It may ignore exact-output instructions and is not a deterministic protocol or tool-use oracle; use `canary-mock/Deterministic Canary` for assertions.
31
+ - The deterministic mock does not implement OMP's compaction-summary contract, so `/compact` reports `OMP compaction failed` in the canary; compaction remains covered by protocol fixtures.
32
+ - `/handoff` reports `OMP command failed` in the controlled canary even with deterministic role models configured; treat handoff as unavailable there until its native prerequisite is isolated.
33
+ - On official Paseo 0.8.0, requesting a live approval-mode change that the plugin rejects can trigger the daemon's unhandled-rejection restart path. Create a new `full`, `write`, or `ask` session instead of changing mode in place.
34
+ - Native Fast mode and a first-class plan mode are not exposed. `/handoff` is implemented but is not reproducible in the controlled canary without its native OMP workflow prerequisites.
35
+ - Terminal-started OMP sessions are discoverable and importable but are not registered automatically through a terminal hook.
36
+ - OMP tool output reaching RPC stdout is strictly parsed and bounded, but channel purity ultimately depends on OMP keeping non-protocol output off stdout.
37
+
38
+ The deduplicated comparison with every known OMP report in `getpaseo/paseo` is maintained in [docs/core-provider-issue-audit.md](docs/core-provider-issue-audit.md).
39
+
40
+ See [TESTING.md](TESTING.md#omp-rpc-compatibility-intake) for the schema-drift intake and regression process.
package/TESTING.md ADDED
@@ -0,0 +1,147 @@
1
+ # OMP provider parity audit
2
+
3
+ Validated against the Paseo plugin SDK versions pinned in `package.json`, official Paseo Docker image `0.8.0@sha256:5518da7cdd35f132e8a944c35e509c677a90a8f3ec8a78df98f7fb5fd5e2c6c3`, and OMP 18.1.15.
4
+
5
+ Classifications:
6
+
7
+ - **Equivalent**: same consumer-observable behavior is implemented by the `omp-plugin` provider.
8
+ - **Unsupported**: OMP has no meaningful native operation for this behavior.
9
+ - **Protocol**: Paseo core owns the behavior for every public provider.
10
+ - **Blocked**: parity needs the linked narrow public API or core change; the plugin fails visibly or provides the documented partial behavior.
11
+
12
+ ## Evidence matrix
13
+
14
+ | Inventory behavior | Classification | Evidence |
15
+ | --- | --- | --- |
16
+ | Binary availability and version diagnostics | **Equivalent** | Provider refresh calls the bounded `checkAvailability` hook with normalized profile options and the host deadline. `server/provider-diagnostics.ts` distinguishes missing, unrunnable, incompatible, and available runtimes without exposing probe output. |
17
+ | Catalog discovery | **Equivalent** | `server/provider/catalog.ts::discoverOmpCatalog` launches with the normalized profile command, environment policy, parameters, workspace, and timeout settings; selected daemon values are resolved only when the catalog child spawns. |
18
+ | Catalog cache identity | **Equivalent** | `getCatalogCacheKey` hashes normalized `providerOptions`, including configured `inheritEnv` names but never their values or secret-derived fingerprints, plus profile `settings`, scope, working directory, and the effective default command. Core applies configured `models` and `additionalModels` per profile. |
19
+ | Default mode | **Equivalent** | Catalog reports `defaultMode: full`; `full` is always available, while bundled `write`/`ask` modes are advertised only after `permission` negotiation. Covered by `tests/provider.test.ts` and `tests/provider-options.test.ts`. |
20
+ | Create session | **Equivalent** | `OmpProviderSession.open`; ordered `session.opened`, committed `session.config`, and `session.ready` regression. |
21
+ | Resume session | **Equivalent** | Versioned plugin persistence validates the native session ID, authorizes cwd against the configured `providerOptions.params.sessionDir`, resumes exactly, and replays before ready. Covered by `tests/provider.test.ts` and `tests/provider-conformance.test.ts`. |
22
+ | List sessions | **Equivalent** | `session.list` consumes profile-scoped `providerOptions` and `settings`, scans the configured session root, and returns separate bounded first- and last-user-prompt previews. |
23
+ | Import session | **Protocol** and **Equivalent** | `session.list` plus persistence replay feeds the generic plugin-provider import path and creates an `omp-plugin` agent without changing bundled-provider records. |
24
+ | Archive/unarchive | **Unsupported** | OMP has no native archive operation. Neither adapter mutates OMP transcripts; Paseo still archives its own agent record. |
25
+ | Working directory | **Equivalent** | Absolute cwd validation, launch forwarding, resume ownership checks, session-list scoping, and host-tool cwd tests. |
26
+ | Environment propagation | **Equivalent** | `config-normalization.ts` overlays session env on profile env; `omp-rpc.ts::buildOmpEnvironment` allowlists inherited runtime/provider variables and rejects loader/path injection. |
27
+ | System prompts | **Protocol** and **Equivalent** | Core combines agent and daemon prompts before `session.open`; plugin forwards one bounded `--append-system-prompt`. Profile/recovery tests preserve it. |
28
+ | Persisted sessions | **Equivalent** | Versioned opaque plugin persistence, transcript reservation, replay, process recovery, and cleanup quarantine regressions are covered in `tests/provider.test.ts` and `tests/provider-conformance.test.ts`. |
29
+ | Ephemeral sessions | **Equivalent** | `persist: false` maps to `--no-session`; runtime loss fails visibly instead of inventing a resume handle. |
30
+ | Internal sessions | **Protocol** and **Equivalent** | Metadata generation requests `persistSession: false`; the generic plugin host maps that to `config.persist: false`, then OMP receives `--no-session`. |
31
+ | Text prompts | **Equivalent** | Bounded multipart text joins and native prompt lifecycle coverage in `tests/provider.test.ts`. |
32
+ | Image prompts | **Equivalent** | Valid native image models receive image blocks; text-only models receive private content-addressed local files with an aggregate cap and turn/session/failure cleanup. The file path is valid because the direct provider and OMP child share the daemon host. |
33
+ | Structured attachments | **Equivalent** | Forge change requests/issues, legacy GitHub forms, text, reviews, and uploaded files render to bounded OMP prompt text; regression in `tests/provider.test.ts`. |
34
+ | Optimistic message correlation | **Equivalent** | Native entry lookup, repeated-text occurrence ownership, steering correlation, replay-boundary dedupe, and exactly-one `session.prompt_result` regressions. |
35
+ | Streaming assistant text | **Equivalent** | `OmpTimelineProjector` publishes stable complete snapshots with frame coalescing and bounded retained bytes. |
36
+ | Streaming reasoning | **Equivalent** | Indexed thinking blocks map to stable `reasoning` items and share stream bounds. |
37
+ | `contentIndex` ordering | **Equivalent** | Stable 0→1→0 updates, sparse-index rejection, and 64-block bounds are tested in `tests/provider.test.ts`. |
38
+ | Tool lifecycle | **Equivalent** | Running/update/terminal snapshots, mapped shell/read/edit/write/search/fetch/subagent details, ID reuse defense, and terminal cleanup are covered. |
39
+ | Todo lifecycle | **Equivalent** | Todo tool results and reminder/auto-clear events reduce to one stable `omp:todos` item; malformed inputs degrade safely. |
40
+ | Compaction events | **Equivalent** | Manual and automatic operations retain IDs, distinguish retry/skipped/canceled/failed states, flush streams, and refresh usage. |
41
+ | Custom messages | **Equivalent** | Displayable custom and bash-execution messages map to typed or fallback items; `display: false` remains hidden. |
42
+ | Advisor messages | **Equivalent** | Advisor notes preserve severity/attribution in stable tool-call blocks; `advisor_yielded` emits completion notice. |
43
+ | System notices | **Equivalent** | Native notices and safe passive UI notifications map to bounded notification items; hidden custom notices remain hidden. |
44
+ | Ask interactions | **Equivalent** | Select/confirm/input/editor questions, option descriptions, fixed-only rejection, bounded freeform input, native sentinel/follow-up submission, cancellation, timeout, and turn ownership are covered by permission regressions. |
45
+ | Typed tool permission presentation | **Equivalent** | OMP `typedToolApprovals: 1` is negotiated reciprocally; strict native request/cancel/response frames are correlated exactly once and mapped from trusted shell/edit/write identity to bounded, content-preserving `kind: "tool"` permissions. Older OMP builds retain generic extension questions. |
46
+ | Slash command catalog | **Equivalent** | Native commands and aliases refresh authoritatively; the bundled `compact`, `autocompact`, `handoff`, `steer`, and `follow-up` commands are always published. |
47
+ | Manual `/compact` | **Equivalent** | Uses native `compact`, exposes one loading/completed operation, keeps long requests alive, refreshes usage, and supports interruption. |
48
+ | `/autocompact` | **Equivalent** | `on`, `off`, and state-backed `toggle` use native `set_auto_compaction`; invalid or unavailable state fails visibly. |
49
+ | `/handoff` | **Equivalent** | Structured command calls native `handoff` with optional instructions and owns a provider turn until native assistant/tool/permission/terminal events settle. |
50
+ | `/follow-up` | **Equivalent** | Structured command sends native `follow_up` and owns a provider turn rather than publishing an immediate synthetic completion. |
51
+ | Native steering | **Equivalent** | `delivery: steer` and `/steer` use native steering, correlate one user row, preserve active-turn ownership, and reject stale/terminal targets. |
52
+ | Interrupt | **Equivalent** | Native abort, exactly-one terminal event, in-flight tool/child retirement, permission cleanup, and concurrent-close serialization are covered. |
53
+ | Model selection | **Equivalent** | Catalog-backed opaque public IDs map to native provider/model IDs; committed state is re-read after open/configure/recovery. |
54
+ | Thinking selection | **Equivalent** | Model-specific effort lists, defaults, runtime changes, invalid selections, and recovery are covered. |
55
+ | Mode selection | **Equivalent** | `full`/`write`/`ask` map to `yolo`/`write`/`always-ask`; catalogs and opens expose interactive modes only when `permission` was negotiated, and live changes retain the bundled “new session” constraint. |
56
+ | Retry fallback events | **Equivalent** | Fallback telemetry renders bounded status items and triggers committed model/thinking refresh without trusting event strings as state. |
57
+ | Context and token usage | **Equivalent** | Periodic, post-compaction, fallback, terminal, timeout, and stale-generation samples publish `session.usage`. |
58
+ | Native host tools | **Equivalent** | Paseo's caller-scoped MCP endpoint is converted to OMP host tools with workspace identity, progress, cancellation, bounded results, and ownership cleanup. |
59
+ | Configured MCP servers | **Equivalent** | stdio/http/SSE transports are opened by the plugin host process, namespaced, paginated, bounded, and passed through OMP's native host-tool API. |
60
+ | Subagents | **Equivalent** | Live lifecycle/progress/events create provider-owned child sessions; subscription failure removes the negotiated capability. |
61
+ | Nested child timelines | **Equivalent** | Child/grandchild ancestry, stable IDs, live restart, cold transcript replay, terminal states, and cumulative replay bounds are covered. |
62
+ | Conversation rewind | **Equivalent** | Only conversation scope is advertised; active-turn/stale-token rejection and branch replay are tested. |
63
+ | Native branch transition | **Equivalent** | New native IDs atomically replace persistence/reservation ownership; indeterminate post-branch failures close and quarantine the session. |
64
+ | Custom executable command | **Equivalent** | Strict `providerOptions.command` replaces the executable prefix and survives recovery. |
65
+ | Custom environment | **Equivalent** | Strict `providerOptions.env` is merged below launch env and survives recovery. Strict `providerOptions.inheritEnv` stores bounded names only and resolves unshadowed daemon values at catalog/session spawn. Unexpected or internal launch failures use fixed fallbacks rather than serializing launch configuration, while explicit public validation errors may include caller-supplied configuration names or values. |
66
+ | Configured output redaction | **Equivalent** | `providerOptions.outputRedaction` defaults to `none`. `configured-values` performs bounded best-effort literal replacement for explicitly supplied credential values and every non-empty value selected through `inheritEnv`, regardless of its name, across root and nested timelines; generated, encoded, transformed, and independently streamed fragments remain outside its scope. |
67
+ | Provider parameters | **Equivalent** | `sessionDir`, `rpcTimeoutMs`, and `smol`/`slow`/`plan` model roles map to native arguments and survive recovery; cold resume authorization scans the normalized configured `sessionDir`. |
68
+ | Configured model replacement/additions | **Protocol** and **Equivalent** | Paseo applies profile `models` replacement and `additionalModels` overlays to the profile-specific plugin catalog. |
69
+ | Generic denied tools | **Equivalent** | Core forwards a bounded, deduplicated `deniedTools` list; the plugin turns recognized OMP built-ins into an explicit launch allow-list before the process starts and rejects unknown names rather than silently under-enforcing. |
70
+ | Strict provider option validation before launch | **Equivalent** | The provider registers `OmpProviderOptionsSchema`, including bounded `inheritEnv` names; core normalizes once and forwards the same names-only value through availability, cache identity, discovery, listing, and launch. |
71
+ | Profile settings in provider discovery | **Equivalent** | Core forwards profile settings to catalog, cache-key, and session-list operations; the provider includes them in cache identity without claiming unsupported live OMP settings. |
72
+ | Terminal-started OMP session hooks | **Unsupported** | The bundled terminal hook registry contains Claude, Codex, and OpenCode only; OMP exposes no registered terminal activity hook to preserve. |
73
+ | Metadata-generation flows | **Protocol** and **Equivalent** | Generic structured generation selects plugin models from catalog metadata and creates non-persisted sessions; provider responses use the existing parse/validation retry loop. |
74
+
75
+ The output boundary validates protocol shapes and bounds strings, structured values, depth, item counts, nodes, cumulative serialized bytes, and cycles. With the default `outputRedaction: "none"`, it does not heuristically redact or rewrite native OMP, model, tool, or permission content. The optional `configured-values` mode replaces exact configured credential literals on a best-effort basis, but it cannot detect generated secrets or encoded, transformed, or independently streamed fragments. Credentials therefore must not appear in prompts or tool output, and centralized Paseo policy is required for redaction guarantees. Unexpected or internal launch failures use fixed public fallbacks rather than serializing launch configuration, while explicit public validation errors may include caller-supplied configuration names or values.
76
+
77
+ ## Integration boundary tests
78
+
79
+ The provider identity boundary is permanent: `server/provider/registration.ts` registers only `omp-plugin`, while diagnostics recognize both the bundled `omp` provider and this plugin. `tests/server-bundle.test.ts` verifies the packaged server contribution keeps that identity. No test removes, replaces, aliases, migrates, or assumes ownership of the bundled provider.
80
+
81
+ Run the real Docker host/container ownership boundary test with:
82
+
83
+ ```sh
84
+ npm run test:integration:docker
85
+ ```
86
+
87
+ The script starts the MCP server as a host process, calls it from a Node container, executes a host tool, and verifies the returned host PID, host working directory, caller agent ID, and workspace ID. Set `PASEO_OMP_DOCKER_IMAGE` to override the default `node:22.22.1-bookworm-slim` image.
88
+
89
+ Run the equivalent Windows-host/WSL boundary with:
90
+
91
+ ```sh
92
+ npm run test:integration:wsl
93
+ ```
94
+
95
+ The WSL script skips when `wsl.exe` or WSL Node is unavailable. Set `PASEO_OMP_REQUIRE_WSL=1` to make either condition fatal, as CI does. `PASEO_OMP_WSL_NODE` may override the default `node` executable inside WSL.
96
+
97
+ The Linux `paseo-omp real OMP 18.1.15` CI job downloads the pinned `omp-linux-x64` release asset, verifies SHA-256 `747518a41fbb32ac47491b4677a7a921d0d9e5977ae006c358d6836813149adc`, and runs `PASEO_OMP_REAL_E2E=1 npm test -- tests/provider.real.e2e.test.ts`. The test uses the real OMP binary and a local deterministic OpenAI-compatible model endpoint, so catalog and text-plus-Bash execution are mandatory without repository secrets.
98
+
99
+ The controlled canary in `canary/compose.yml` builds this plugin into that official Paseo image and installs checksummed OMP binaries for `amd64` and `arm64`. `canary/smoke.ts` passed catalog/mode discovery, text and image prompts, Bash, a configured stdio MCP tool, permission allow/deny/cancel, steering, interruption, session listing/import/resume, subagents, conversation rewind followed by another turn, Hub process visibility, usage, and every plugin RPC. Browser verification loaded the global OMP health/configuration surface and the agent-scoped Hub, Memory, and Sessions controls. The default mock returned `CANARY_MOCK_OK`, `CANARY_TOOL_OK`, and `CANARY_MCP_OK`; the optional Ollama profile pulled `qwen2.5:0.5b` and completed turns without paid credentials. The harness records the canary-only `/compact` and `/handoff` failures instead of masking them.
100
+
101
+ ## OMP RPC compatibility intake
102
+
103
+ Treat every upstream `rpc-ui` change as explicit compatibility work. Do not widen a Zod schema with `passthrough`, `unknown`, or an optional field merely to accept a new frame.
104
+
105
+ 1. Open an issue with the [OMP RPC compatibility template](https://github.com/omercnet/paseo-plugins/issues/new?template=omp-rpc-compatibility.yml). Record exact OMP, plugin, Paseo daemon, and Paseo app versions; the negotiated protocol and capabilities; the smallest reproduction; and sanitized frame shapes. Never attach credentials, private paths, prompts, or transcripts.
106
+ 2. Reproduce against both the reported OMP revision and the pinned minimum-tested `omp/18.1.15` binary. Classify the change as additive optional, additive required, removed or renamed, type or semantic change, or negotiation change.
107
+ 3. Compare the affected ready, request, response, or event shape with the strict schemas in `server/provider/omp-rpc.ts`. Decide whether the plugin can support both contracts without ambiguity. A breaking contract requires an explicit compatibility decision and changelog entry, not silent coercion.
108
+
109
+ Required drift is release-blocking. If OMP adds a mandatory frame, removes or renames a required method or field, or changes an existing field's meaning, keep the strict parser and make session startup or the active request fail visibly. Do not silently discard the frame, make the requirement optional, or route around negotiation. Resume release work only after both sides have an explicit compatible contract, fixtures, focused regressions, and a real-binary result.
110
+ 4. Add the changed frame to `tests/fixtures/fake-omp.ts`, then add a focused regression for acceptance, rejection, negotiation, cancellation, and bounds as applicable. Capability changes must cover both reciprocal negotiation and the absent-capability fallback. Typed approval drift must retain the generic extension-question path when `typedToolApprovals: 1` is not negotiated.
111
+ 5. Run `npm test -- tests/omp-rpc.test.ts tests/provider.test.ts`, `npm run test:coverage`, and `PASEO_OMP_REAL_E2E=1 npm test -- tests/provider.real.e2e.test.ts` with the candidate OMP binary.
112
+ 6. Update the minimum-tested version only after the real-binary job is pinned to that release and its SHA-256, the compatibility issue links the evidence, and the README, support matrix, CI job name, fixture version, and changelog agree.
113
+
114
+ The plugin maintainer owns triage and adaptation. Escalate an isolated OMP implementation defect upstream and an isolated Paseo SDK or provider-protocol defect to Paseo, while keeping the cross-project regression in this repository.
115
+
116
+ ## Release publication
117
+
118
+ `release-please.yml` only creates release metadata and forwards the created `paseo-omp` tag and commit SHA to `publish-paseo-omp.yml`. The metadata job never checks out or executes repository code. The publisher has no pull-request permission, resolves the tag independently, and waits for a successful `CI` push run whose `head_sha` is exactly the tagged commit before checkout or execution.
119
+
120
+ The publisher builds the self-contained ZIP, writes its corruption-detection checksum, creates a signed GitHub build-provenance attestation for both files, rechecks that the tag still resolves to the gated SHA, and uploads with `--clobber`. To recover an interrupted upload, dispatch **Publish Paseo OMP** with the existing `paseo-omp-v<version>` tag; the same identity and CI gates apply, so reruns are idempotent.
121
+
122
+ The SHA-256 file is not a signature. Consumers authenticate the archive with `gh attestation verify` and may use the checksum only to detect accidental transfer or storage corruption.
123
+
124
+ ### Alpha release channel
125
+
126
+ `release-please-config.json` sets only `paseo-omp` to `prerelease: true` with `prerelease-type: alpha`. With the existing `initial-version: 0.1.0` and manifest version `0.0.0`, the first release PR is expected to prepare `0.1.0-alpha.1`, tag it as `paseo-omp-v0.1.0-alpha.1`, and mark the GitHub release as a prerelease. Other monorepo components keep their existing stable release behavior.
127
+
128
+ Do not manually edit `paseo-omp/package.json` or `.release-please-manifest.json` before that release PR; Release Please must update both atomically. After alpha validation, remove `prerelease` and `prerelease-type` from the `paseo-omp` package config and let Release Please prepare stable `0.1.0`. Never retag an alpha commit as stable.
129
+
130
+ The go/no-go criteria, manual acceptance boundary, and alpha limitation list are maintained in [docs/alpha-release-checklist.md](docs/alpha-release-checklist.md). Preparation does not authorize a push, tag, GitHub release, or publication; explicit maintainer approval after testing is required.
131
+
132
+ ## Audit verification
133
+
134
+ - `npm run check`: clean across 84 files.
135
+ - `npm run typecheck`: clean.
136
+ - `npm test`: full Vitest suite passed, with environment-gated scenarios skipped when their runtimes were unavailable.
137
+ - `npm test -- tests/provider-conformance.test.ts`: host-boundary conformance coverage includes `prompt.command`, `session.configure`, typed and fallback permission allow/deny/cancel paths, registry-driven reload/removal, verified stubborn-descendant cleanup, and sequential turns plus interrupt races with post-turn barriers.
138
+ - `PASEO_OMP_REAL_E2E=1 npm test -- tests/provider.real.e2e.test.ts`: the installed `omp/18.1.15` catalog and hermetic real-binary text/Bash scenarios run against a local deterministic model.
139
+ - `npm run test:coverage`: Vitest enforces aggregate 85% function and 89% line coverage over loaded source modules. Generated `dist/**` trees are excluded.
140
+ - `npm run package:release`: `dist/paseo-omp-v0.0.0.zip` builds from tracked allowlisted source plus its closed production/compiler dependency set.
141
+ - `npm run test:integration:install`: the self-contained archive imports dependencies and compiles with an unreachable proxy; a fresh Git-style checkout installs with lifecycle scripts disabled, retains required runtime packages, typechecks, bundles both entries, and loads the server contribution.
142
+ - `npm run test:integration:docker`: verifies the host/container ownership boundary.
143
+ - `npm run test:integration:wsl`: locally skips when `wsl.exe` is unavailable; Windows CI sets `PASEO_OMP_REQUIRE_WSL=1`, so this boundary remains required there.
144
+ - `docker compose -f canary/compose.yml`: official Paseo 0.8.0, deterministic mock, Tailscale-bound web UI, and optional Ollama `qwen2.5:0.5b` passed end-to-end.
145
+ - `mise x actionlint@1.7.12 -- actionlint .github/workflows/*.yml`: passed.
146
+ - `zizmor .github/workflows`: no findings (offline audit; six repository-wide suppressions remain).
147
+ - Release Please 17.1.2 `config.json` and `manifest.json` schema validation: passed for `release-please-config.json` and `.release-please-manifest.json`.
@@ -0,0 +1,12 @@
1
+ import type { PluginButtonIconProps } from "@getpaseo/plugin/client";
2
+ import { Text, View } from "react-native";
3
+
4
+ // omp (oh-my-pi) uses the option/alt-key glyph "⌥" as its mark; there is no separate
5
+ // logo asset to bundle, so the pill renders the glyph directly at the host's icon size.
6
+ export function OmpIcon({ size, color }: PluginButtonIconProps) {
7
+ return (
8
+ <View style={{ width: size, height: size, alignItems: "center", justifyContent: "center" }}>
9
+ <Text style={{ fontSize: size * 0.85, lineHeight: size, color, fontWeight: "600" }}>⌥</Text>
10
+ </View>
11
+ );
12
+ }
@@ -0,0 +1,132 @@
1
+ import { type PluginButtonContentProps, useAgent, useRpc } from "@getpaseo/plugin/client";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { useMemo, useState } from "react";
4
+ import { Pressable, Text, View } from "react-native";
5
+ import { type HubProcess, listHubProcesses, tailHubLog } from "../shared/hub";
6
+ import { type HubProcessTone, hubProcessTone } from "./hub-status";
7
+
8
+ const PROCESS_POLL_MS = 3_000;
9
+ const LOG_POLL_MS = 2_000;
10
+
11
+ function age(timestamp: number | null): string {
12
+ if (timestamp === null) return "";
13
+ const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / 1_000));
14
+ if (seconds < 60) return `${seconds}s`;
15
+ const minutes = Math.floor(seconds / 60);
16
+ if (minutes < 60) return `${minutes}m`;
17
+ const hours = Math.floor(minutes / 60);
18
+ if (hours < 24) return `${hours}h`;
19
+ return `${Math.floor(hours / 24)}d`;
20
+ }
21
+
22
+ function command(process: HubProcess): string {
23
+ return [process.application, ...process.args].join(" ");
24
+ }
25
+
26
+ function toneColor(
27
+ tone: HubProcessTone,
28
+ colors: PluginButtonContentProps["theme"]["colors"],
29
+ ): string {
30
+ if (tone === "danger") return colors.statusDanger;
31
+ if (tone === "warning") return colors.statusWarning;
32
+ if (tone === "success") return colors.statusSuccess;
33
+ return colors.foregroundMuted;
34
+ }
35
+
36
+ export function HubPopover(props: PluginButtonContentProps) {
37
+ const { theme, layout } = props;
38
+ const agentId = props.context === "agent" ? props.agentId : "";
39
+ const cwd = useAgent(agentId, (agent) => agent.cwd) ?? "";
40
+ const loadProcesses = useRpc(listHubProcesses);
41
+ const loadLog = useRpc(tailHubLog);
42
+ const [selectedName, setSelectedName] = useState<string | null>(null);
43
+ const processes = useQuery({
44
+ queryKey: ["paseo-omp", "processes", cwd],
45
+ queryFn: () => loadProcesses({ cwd }),
46
+ enabled: cwd.length > 0,
47
+ refetchInterval: PROCESS_POLL_MS,
48
+ });
49
+ const log = useQuery({
50
+ queryKey: ["paseo-omp", "log", cwd, selectedName],
51
+ queryFn: () => loadLog({ cwd, name: selectedName ?? "" }),
52
+ enabled: cwd.length > 0 && selectedName !== null,
53
+ refetchInterval: LOG_POLL_MS,
54
+ });
55
+ const styles = useMemo(
56
+ () => ({
57
+ root: { gap: layout.compact ? 8 : 10 },
58
+ muted: { color: theme.colors.foregroundMuted, fontSize: 13 },
59
+ error: { color: theme.colors.statusDanger, fontSize: 13 },
60
+ card: {
61
+ gap: 4,
62
+ padding: layout.compact ? 10 : 12,
63
+ borderRadius: 10,
64
+ borderWidth: 1,
65
+ borderColor: theme.colors.border,
66
+ backgroundColor: theme.colors.surface1,
67
+ },
68
+ row: { flexDirection: "row" as const, justifyContent: "space-between" as const, gap: 12 },
69
+ name: { color: theme.colors.foreground, fontSize: 14, fontWeight: "600" as const, flex: 1 },
70
+ command: { color: theme.colors.foregroundMuted, fontSize: 12 },
71
+ log: {
72
+ color: theme.colors.foreground,
73
+ fontFamily: "monospace",
74
+ fontSize: 11,
75
+ padding: 10,
76
+ borderRadius: 8,
77
+ backgroundColor: theme.colors.surface2,
78
+ },
79
+ }),
80
+ [layout.compact, theme],
81
+ );
82
+
83
+ if (processes.isLoading) return <Text style={styles.muted}>Loading hub processes…</Text>;
84
+ if (processes.error) return <Text style={styles.error}>Could not read omp hub state.</Text>;
85
+ const items = processes.data?.processes ?? [];
86
+ if (items.length === 0) return <Text style={styles.muted}>No hub-supervised processes.</Text>;
87
+
88
+ return (
89
+ <View style={styles.root}>
90
+ {items.map((process) => {
91
+ const tone = hubProcessTone(process);
92
+ const selected = selectedName === process.name;
93
+ const timestamp = process.exitedAt ?? process.startedAt ?? process.createdAt;
94
+ return (
95
+ <Pressable
96
+ key={process.name}
97
+ accessibilityRole="button"
98
+ accessibilityLabel={`${selected ? "Hide" : "Show"} logs for ${process.name}`}
99
+ onPress={() => setSelectedName(selected ? null : process.name)}
100
+ style={styles.card}
101
+ >
102
+ <View style={styles.row}>
103
+ <Text numberOfLines={1} style={styles.name}>
104
+ {process.name}
105
+ </Text>
106
+ <Text style={{ color: toneColor(tone, theme.colors), fontSize: 12 }}>
107
+ {process.state}
108
+ {process.exitCode !== null ? ` · ${process.exitCode}` : ""}
109
+ {timestamp !== null ? ` · ${age(timestamp)}` : ""}
110
+ </Text>
111
+ </View>
112
+ <Text numberOfLines={2} style={styles.command}>
113
+ {command(process)}
114
+ </Text>
115
+ {selected ? (
116
+ log.isLoading ? (
117
+ <Text style={styles.muted}>Loading logs…</Text>
118
+ ) : log.error ? (
119
+ <Text style={styles.error}>Could not read this process log.</Text>
120
+ ) : (
121
+ <Text selectable style={styles.log}>
122
+ {log.data?.truncated ? "… showing the latest 64 KiB\n" : ""}
123
+ {log.data?.content || "No output."}
124
+ </Text>
125
+ )
126
+ ) : null}
127
+ </Pressable>
128
+ );
129
+ })}
130
+ </View>
131
+ );
132
+ }
@@ -0,0 +1,29 @@
1
+ import type { HubProcess } from "../shared/hub";
2
+
3
+ export type HubProcessTone = "success" | "warning" | "danger" | "muted";
4
+
5
+ const FAILED_STATES: Record<string, true> = { crashed: true, error: true, failed: true };
6
+ const ACTIVE_STATES: Record<string, true> = { ready: true, running: true };
7
+ const TERMINAL_STATES: Record<string, true> = { exited: true, stopped: true };
8
+
9
+ export function hubProcessTone(process: HubProcess): HubProcessTone {
10
+ const state = process.state.toLowerCase();
11
+ if (FAILED_STATES[state] || (process.exitCode !== null && process.exitCode !== 0)) {
12
+ return "danger";
13
+ }
14
+ if (ACTIVE_STATES[state]) return "success";
15
+ if (TERMINAL_STATES[state]) return "muted";
16
+ return "warning";
17
+ }
18
+
19
+ export function summarizeHubProcesses(processes: readonly HubProcess[]): {
20
+ visible: boolean;
21
+ label: string;
22
+ } {
23
+ if (processes.length === 0) return { visible: false, label: "Hub" };
24
+ const hasFailure = processes.some((process) => hubProcessTone(process) === "danger");
25
+ return {
26
+ visible: true,
27
+ label: `Hub · ${processes.length}${hasFailure ? " !" : ""}`,
28
+ };
29
+ }