@omercnet/paseo-omp 0.3.0-next.108.1 → 0.3.0-next.109.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.
- package/README.md +5 -5
- package/SUPPORT.md +3 -4
- package/client/omp-config-surface.tsx +163 -14
- package/client/omp-config-views.ts +24 -0
- package/client/support-diagnostics-state.ts +45 -0
- package/docs/alpha-release-checklist.md +1 -1
- package/docs/installation.md +4 -15
- package/index.server.ts +24 -2
- package/package.json +1 -1
- package/paseo-plugin.json +1 -1
- package/server/operational-failure-diagnostics.ts +76 -0
- package/server/package-version.ts +2 -0
- package/server/protocol-violation-diagnostics.ts +169 -0
- package/server/provider/connection.ts +25 -0
- package/server/provider/host-tools.ts +63 -10
- package/server/provider/omp-rpc.ts +469 -44
- package/server/provider/profile-providers.ts +7 -2
- package/server/provider/registration.ts +12 -2
- package/server/provider/session.ts +70 -3
- package/server/support-diagnostics.ts +284 -0
- package/shared/support-diagnostics.ts +32 -0
package/README.md
CHANGED
|
@@ -6,14 +6,14 @@ Community OMP integration for Paseo. The plugin registers the distinct `omp-plug
|
|
|
6
6
|
|
|
7
7
|
## Quick start
|
|
8
8
|
|
|
9
|
-
Requirements: Paseo `>=0.
|
|
9
|
+
Requirements: Paseo `>=0.9.0-beta.1 <0.10.0`, OMP `18.1.15` or newer, and OMP RPC protocol v2. Paseo 0.9 is required for the Help report's supported copy and external-link APIs, nested provider-subagent ancestry, spawning-tool links, npm-managed installation, and the complete client integration.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
paseo plugin install npm:@omercnet/paseo-omp@<version>
|
|
13
13
|
paseo plugin ls paseo-omp
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Open the **OMP** sidebar to review the provider-profile contract, choose which composer pills appear, browse and edit native scalar settings, manage OMP-native plugins, verify runtime health, and
|
|
16
|
+
Open the **OMP** sidebar to review the provider-profile contract, choose which composer pills appear, browse and edit native scalar settings, manage OMP-native plugins, verify runtime health, and generate a bounded support report under **Help**. The same tabs are available in the workspace OMP panel, except for the global Composer preference tab. Then create an agent and select **OMP Plugin**. Its optional **MCP** composer control runs OMP-native management commands in the current session; setup questions and authorization stay in that chat timeline, where OAuth can open in a workspace-scoped Paseo Browser or on the current device. Composer pill preferences are shared by clients connected to the same host; normal use requires no other plugin-specific settings.
|
|
17
17
|
|
|
18
18
|
- [Install, update, rollback, and local development](docs/installation.md)
|
|
19
19
|
- [Configuration and every provider option](docs/configuration.md)
|
|
@@ -22,13 +22,13 @@ Open the **OMP** sidebar to review the provider-profile contract, choose which c
|
|
|
22
22
|
- [Core-provider issue and parity audit](docs/core-provider-issue-audit.md)
|
|
23
23
|
- [Alpha release checklist](docs/alpha-release-checklist.md)
|
|
24
24
|
|
|
25
|
-
The plugin uses public Paseo 0.
|
|
25
|
+
The plugin uses public Paseo 0.9 beta provider and client contracts and registers distinct `omp-plugin` and `omp-plugin-<profile>` identities; it does not modify the bundled `omp` provider.
|
|
26
26
|
|
|
27
27
|
## Named OMP profiles
|
|
28
28
|
|
|
29
29
|
At startup the plugin discovers directory names under `~/.omp/profiles/` (or `PI_CONFIG_DIR`) and registers **OMP · <profile>** for each valid lowercase name. Select that provider when creating a profile-backed agent. Discovery, launch, recovery and persisted session listing share its fixed profile and session root. Matching command wrappers, including Doppler, remain supported; conflicting profile or session-directory overrides fail before launch. Plain `env NAME=value omp` wrappers are supported, but environment-control flags such as `env -i` and `env -u` are rejected because they can discard the selected store. Profile-specific XDG roots cannot be overridden by per-launch environment values. Reload the plugin after adding a profile.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Separate provider identities let the model picker request the correct profile catalog before an agent exists. Existing `omp-plugin` and bundled `omp` agents retain their provider; they are not migrated automatically. Profile names use OMP's lowercase grammar, including dotted names such as `team.prod`.
|
|
32
32
|
|
|
33
33
|
The OMP sidebar and memory panel expose an explicit store selector. Settings, plugins, quota, history, memory and diagnostics use that selection, and profile-agent popovers derive it from the provider ID. OMP-compatible XDG data and state roots are honored when a migrated profile uses them. Default-provider views are labelled **Daemon default store**; changing `providerOptions.command` on the default provider does not make those views profile-aware. Workspace configuration remains project-scoped, with the selected profile supplying inherited settings. Hub records are daemon-wide. The RPCs also accept an explicit absolute `store.agentDir` for custom stores; it is mutually exclusive with `store.profile`.
|
|
34
34
|
|
|
@@ -99,7 +99,7 @@ Existing agents whose provider is `omp` remain owned by the bundled provider. Ne
|
|
|
99
99
|
|
|
100
100
|
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. For terminal completion, a matching `agent_end.requestId` is authoritative and mismatches are ignored before state changes. Released OMP 18.2.x builds that omit that field use an ordered fallback requiring a fresh correlated native user entry followed by current-turn assistant activity, an idle non-compacting runtime, and no conflicting permission, tool, steer, or child-session work. Ambiguous events while OMP is active are ignored; unresolved ambiguity after confirmed idle fails only the Paseo turn. This trades a bounded residual same-agent stale-event risk for compatibility with released OMP instead of terminating and lazily restarting the runtime after every later prompt. Paseo clients, including mobile clients inside reconnect grace, do not own or terminate the daemon-managed OMP session.
|
|
101
101
|
|
|
102
|
-
Paseo 0.9.0-beta.1 is the verified canary target and
|
|
102
|
+
Paseo 0.9.0-beta.1 is the verified canary target and minimum supported release. It preserves nested provider-subagent ancestry and spawning-tool links, and provides npm-managed plugin sources, platform-owned external URL opening, and whole-item timeline transforms before Overview grouping. The required full Docker canary passes there with OMP 18.1.15 and 18.2.0.
|
|
103
103
|
|
|
104
104
|
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.
|
|
105
105
|
|
package/SUPPORT.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
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
6
|
|
|
7
|
-
Report plugin packaging, installation, provider
|
|
7
|
+
Report general plugin packaging, installation, UI, provider, replay, tool, terminal, and compatibility failures with the [paseo-omp bug template](https://github.com/omercnet/paseo-plugins/issues/new?template=omp-plugin.yml). Open **OMP → Help**, refresh the bounded diagnostic report, review it, and paste it into the issue. Use the narrower [OMP RPC compatibility template](https://github.com/omercnet/paseo-plugins/issues/new?template=omp-rpc-compatibility.yml) for confirmed native protocol changes.
|
|
8
8
|
|
|
9
9
|
After the failure is isolated:
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ Do not put credentials, private repository paths, session transcripts, or unreda
|
|
|
16
16
|
|
|
17
17
|
## Supported versions
|
|
18
18
|
|
|
19
|
-
- Paseo: `>=0.
|
|
19
|
+
- Paseo: `>=0.9.0-beta.1 <0.10.0` on both the daemon and every app loading the client entry. The 0.9 floor provides the supported plugin copy and external-link APIs as well as nested provider-subagent ancestry and spawning-tool links.
|
|
20
20
|
- OMP: `18.1.15` is the oldest supported release. The required full Docker canary passes with 18.1.15 and 18.2.0; the hard runtime contract remains `rpc-ui` protocol v2, not the version string alone.
|
|
21
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
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.
|
|
@@ -32,8 +32,7 @@ Do not put credentials, private repository paths, session transcripts, or unreda
|
|
|
32
32
|
- `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.
|
|
33
33
|
- 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.
|
|
34
34
|
- `/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.
|
|
35
|
-
-
|
|
36
|
-
- Paseo 0.8 clients do not expose the platform-owned plugin URL opener and transform timeline items after Overview grouping. Use a 0.9.0-beta.1 app for external documentation/device-authorization actions and for reliable image-card transformation of every source tool call.
|
|
35
|
+
- Paseo 0.9.0-beta.1 or newer is required for platform-owned plugin URL opening and reliable image-card transformation before Overview grouping.
|
|
37
36
|
- 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.
|
|
38
37
|
- Terminal-started OMP sessions are discoverable and importable but are not registered automatically through a terminal hook.
|
|
39
38
|
- Paseo clients do not own OMP sessions. Mobile or desktop disconnects remain subject to Paseo's reconnect grace while the daemon-owned provider and OMP child continue independently; reconnecting can reveal an existing turn but does not transfer or sever runtime ownership.
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
useRpc,
|
|
6
6
|
useWorkspace,
|
|
7
7
|
} from "@getpaseo/plugin/client";
|
|
8
|
-
import { Icon, TextInput } from "@getpaseo/plugin/client/react-native";
|
|
8
|
+
import { copyText, Icon, TextInput } from "@getpaseo/plugin/client/react-native";
|
|
9
|
+
import { ExternalLink } from "@getpaseo/plugin/client/ui";
|
|
9
10
|
import { useIsMutating, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
10
11
|
import type { ReactNode } from "react";
|
|
11
12
|
import { useCallback, useMemo, useState } from "react";
|
|
@@ -27,8 +28,10 @@ import {
|
|
|
27
28
|
} from "../shared/omp-settings";
|
|
28
29
|
import { type OmpStore, storeLabel } from "../shared/omp-store";
|
|
29
30
|
import { getOmpProviderHealth, type OmpProviderHealth } from "../shared/provider-diagnostics";
|
|
31
|
+
import { getOmpSupportReport, OMP_SUPPORT_ISSUE_URL } from "../shared/support-diagnostics";
|
|
30
32
|
import { ComposerPillSettingsSection } from "./composer-pill-settings";
|
|
31
33
|
import { openOmpExternalUrl } from "./external-url";
|
|
34
|
+
import { type OmpConfigSurfaceView as SurfaceView, surfaceViewsForScope } from "./omp-config-views";
|
|
32
35
|
import {
|
|
33
36
|
documentationForSettingCategory,
|
|
34
37
|
documentationForSettingPath,
|
|
@@ -60,6 +63,11 @@ import {
|
|
|
60
63
|
summarizeProviderStatus,
|
|
61
64
|
summarizeRpcUiSupport,
|
|
62
65
|
} from "./provider-diagnostics-state";
|
|
66
|
+
import {
|
|
67
|
+
refreshSupportReport,
|
|
68
|
+
type SupportReportCopyState,
|
|
69
|
+
supportDiagnosticsViewState,
|
|
70
|
+
} from "./support-diagnostics-state";
|
|
63
71
|
|
|
64
72
|
const SETTINGS_QUERY_KEY = ["paseo-omp", "settings"] as const;
|
|
65
73
|
const CONFIG_POLL_MS = 30_000;
|
|
@@ -95,6 +103,10 @@ export interface OmpConfigStyles {
|
|
|
95
103
|
docLinkPressed: ViewStyle;
|
|
96
104
|
docLinkText: TextStyle;
|
|
97
105
|
refreshLabel: TextStyle;
|
|
106
|
+
helpActions: ViewStyle;
|
|
107
|
+
helpReport: ViewStyle;
|
|
108
|
+
helpReportText: TextStyle;
|
|
109
|
+
helpLinkText: TextStyle;
|
|
98
110
|
card: ViewStyle;
|
|
99
111
|
cardHeader: ViewStyle;
|
|
100
112
|
cardTitle: TextStyle;
|
|
@@ -219,6 +231,26 @@ function useConfigStyles(theme: PluginSurfaceProps["theme"], compact: boolean):
|
|
|
219
231
|
backgroundColor: theme.colors.surface1,
|
|
220
232
|
},
|
|
221
233
|
refreshLabel: { color: theme.colors.foreground, fontSize: 13 },
|
|
234
|
+
helpActions: {
|
|
235
|
+
flexDirection: "row",
|
|
236
|
+
alignItems: "center",
|
|
237
|
+
flexWrap: "wrap",
|
|
238
|
+
gap: 8,
|
|
239
|
+
},
|
|
240
|
+
helpReport: {
|
|
241
|
+
maxWidth: "100%",
|
|
242
|
+
padding: compact ? 10 : 12,
|
|
243
|
+
borderWidth: 1,
|
|
244
|
+
borderColor: theme.colors.border,
|
|
245
|
+
borderRadius: 10,
|
|
246
|
+
backgroundColor: theme.colors.surface1,
|
|
247
|
+
},
|
|
248
|
+
helpReportText: {
|
|
249
|
+
color: theme.colors.foreground,
|
|
250
|
+
fontSize: compact ? 11 : 12,
|
|
251
|
+
lineHeight: compact ? 16 : 18,
|
|
252
|
+
},
|
|
253
|
+
helpLinkText: { color: theme.colors.accent, fontSize: 13, fontWeight: "600" },
|
|
222
254
|
cardHeader: {
|
|
223
255
|
flexDirection: "row",
|
|
224
256
|
alignItems: "center",
|
|
@@ -695,6 +727,131 @@ function ProviderHealthSection({
|
|
|
695
727
|
);
|
|
696
728
|
}
|
|
697
729
|
|
|
730
|
+
function SupportDiagnosticsSection({
|
|
731
|
+
theme,
|
|
732
|
+
styles,
|
|
733
|
+
cwd,
|
|
734
|
+
store,
|
|
735
|
+
}: {
|
|
736
|
+
theme: PluginSurfaceProps["theme"];
|
|
737
|
+
styles: OmpConfigStyles;
|
|
738
|
+
cwd?: string;
|
|
739
|
+
store?: OmpStore;
|
|
740
|
+
}) {
|
|
741
|
+
const loadReport = useRpc(getOmpSupportReport);
|
|
742
|
+
const queryClient = useQueryClient();
|
|
743
|
+
const context = { store, ...(cwd ? { cwd } : {}) };
|
|
744
|
+
const queryKey = ["paseo-omp", "support-report", ompStoreKey(store), cwd ?? "global"];
|
|
745
|
+
const report = useQuery({
|
|
746
|
+
queryKey,
|
|
747
|
+
queryFn: () => loadReport(context),
|
|
748
|
+
staleTime: Number.POSITIVE_INFINITY,
|
|
749
|
+
retry: false,
|
|
750
|
+
});
|
|
751
|
+
const refreshReport = useMutation({
|
|
752
|
+
mutationFn: () => refreshSupportReport(loadReport, context),
|
|
753
|
+
onSuccess: (value) => queryClient.setQueryData(queryKey, value),
|
|
754
|
+
});
|
|
755
|
+
const [copyState, setCopyState] = useState<SupportReportCopyState>("idle");
|
|
756
|
+
const [linkError, setLinkError] = useState(false);
|
|
757
|
+
const viewState = supportDiagnosticsViewState({
|
|
758
|
+
loading: report.isLoading,
|
|
759
|
+
refreshing: refreshReport.isPending || (report.isFetching && !report.isLoading),
|
|
760
|
+
hasReport: Boolean(report.data?.report),
|
|
761
|
+
reportFailed: Boolean(report.error || refreshReport.error),
|
|
762
|
+
copyState,
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
const refresh = () => {
|
|
766
|
+
setCopyState("idle");
|
|
767
|
+
refreshReport.mutate();
|
|
768
|
+
};
|
|
769
|
+
const copyReport = async () => {
|
|
770
|
+
if (!report.data?.report) return;
|
|
771
|
+
setCopyState("copying");
|
|
772
|
+
try {
|
|
773
|
+
await copyText(report.data.report);
|
|
774
|
+
setCopyState("copied");
|
|
775
|
+
} catch {
|
|
776
|
+
setCopyState("error");
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
return (
|
|
781
|
+
<>
|
|
782
|
+
<View style={styles.sectionHeader}>
|
|
783
|
+
<Text style={styles.sectionTitle}>Support diagnostics</Text>
|
|
784
|
+
<Text style={styles.muted}>
|
|
785
|
+
Safe OMP runtime, compatibility, storage-state, and protocol counters for maintainers.
|
|
786
|
+
</Text>
|
|
787
|
+
</View>
|
|
788
|
+
<View style={styles.helpActions}>
|
|
789
|
+
<Pressable
|
|
790
|
+
accessibilityRole="button"
|
|
791
|
+
accessibilityLabel="Refresh OMP support report"
|
|
792
|
+
disabled={report.isFetching || refreshReport.isPending}
|
|
793
|
+
onPress={refresh}
|
|
794
|
+
style={styles.refresh}
|
|
795
|
+
>
|
|
796
|
+
<Icon name="RefreshCw" size={14} color={theme.colors.foreground} />
|
|
797
|
+
<Text style={styles.refreshLabel}>{viewState.refreshLabel}</Text>
|
|
798
|
+
</Pressable>
|
|
799
|
+
<Pressable
|
|
800
|
+
accessibilityRole="button"
|
|
801
|
+
accessibilityLabel="Copy OMP support report"
|
|
802
|
+
disabled={viewState.copyDisabled}
|
|
803
|
+
onPress={() => void copyReport()}
|
|
804
|
+
style={[styles.refresh, viewState.copyDisabled ? { opacity: 0.55 } : null]}
|
|
805
|
+
>
|
|
806
|
+
<Icon name="Copy" size={14} color={theme.colors.foreground} />
|
|
807
|
+
<Text style={styles.refreshLabel}>{viewState.copyLabel}</Text>
|
|
808
|
+
</Pressable>
|
|
809
|
+
<ExternalLink
|
|
810
|
+
href={OMP_SUPPORT_ISSUE_URL}
|
|
811
|
+
accessibilityLabel="Create paseo-omp GitHub issue"
|
|
812
|
+
onError={() => setLinkError(true)}
|
|
813
|
+
>
|
|
814
|
+
<Text style={styles.helpLinkText}>Create GitHub issue</Text>
|
|
815
|
+
</ExternalLink>
|
|
816
|
+
</View>
|
|
817
|
+
<Text style={styles.muted}>
|
|
818
|
+
Review before sharing. The report excludes prompts, transcripts, credentials, commands,
|
|
819
|
+
URLs, repository names, and private paths.
|
|
820
|
+
</Text>
|
|
821
|
+
{viewState.loadingMessage ? (
|
|
822
|
+
<Text accessibilityLiveRegion="polite" style={styles.muted}>
|
|
823
|
+
{viewState.loadingMessage}
|
|
824
|
+
</Text>
|
|
825
|
+
) : null}
|
|
826
|
+
{viewState.reportError ? (
|
|
827
|
+
<Text accessibilityRole="alert" style={styles.error}>
|
|
828
|
+
{viewState.reportError}
|
|
829
|
+
</Text>
|
|
830
|
+
) : null}
|
|
831
|
+
{viewState.copyFeedback ? (
|
|
832
|
+
<Text
|
|
833
|
+
accessibilityLiveRegion="polite"
|
|
834
|
+
style={copyState === "error" ? styles.error : styles.muted}
|
|
835
|
+
>
|
|
836
|
+
{viewState.copyFeedback}
|
|
837
|
+
</Text>
|
|
838
|
+
) : null}
|
|
839
|
+
{linkError ? (
|
|
840
|
+
<Text accessibilityRole="alert" style={styles.error}>
|
|
841
|
+
Could not open GitHub. Copy the report and open the issue tracker manually.
|
|
842
|
+
</Text>
|
|
843
|
+
) : null}
|
|
844
|
+
{report.data?.report ? (
|
|
845
|
+
<View style={styles.helpReport}>
|
|
846
|
+
<Text selectable style={styles.helpReportText}>
|
|
847
|
+
{report.data.report}
|
|
848
|
+
</Text>
|
|
849
|
+
</View>
|
|
850
|
+
) : null}
|
|
851
|
+
</>
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
|
|
698
855
|
const CATEGORY_LABELS: Record<OmpSettingCategory, string> = {
|
|
699
856
|
appearance: "Appearance",
|
|
700
857
|
model: "Model",
|
|
@@ -740,16 +897,6 @@ function fallbackSettingsFromConfig(config: OmpConfig | null | undefined): OmpSe
|
|
|
740
897
|
return settings;
|
|
741
898
|
}
|
|
742
899
|
|
|
743
|
-
type SurfaceView = "overview" | "plugin" | "plugins" | "composer" | "configuration" | "diagnostics";
|
|
744
|
-
const SURFACE_VIEWS: readonly { id: SurfaceView; label: string }[] = [
|
|
745
|
-
{ id: "overview", label: "Overview" },
|
|
746
|
-
{ id: "plugin", label: "Plugin" },
|
|
747
|
-
{ id: "plugins", label: "OMP plugins" },
|
|
748
|
-
{ id: "composer", label: "Composer" },
|
|
749
|
-
{ id: "configuration", label: "Configuration" },
|
|
750
|
-
{ id: "diagnostics", label: "Diagnostics" },
|
|
751
|
-
];
|
|
752
|
-
|
|
753
900
|
function formatScalarValue(value: unknown): string {
|
|
754
901
|
if (value === undefined || value === null) return "Not set";
|
|
755
902
|
if (typeof value === "boolean") return value ? "Enabled" : "Disabled";
|
|
@@ -1079,9 +1226,7 @@ function OmpConfigContent({
|
|
|
1079
1226
|
const [documentationError, setDocumentationError] = useState<string | null>(null);
|
|
1080
1227
|
const styles = useConfigStyles(theme, layout.compact);
|
|
1081
1228
|
const normalizedSearch = search.trim().toLocaleLowerCase();
|
|
1082
|
-
const surfaceViews = cwd
|
|
1083
|
-
? SURFACE_VIEWS.filter((candidate) => candidate.id !== "composer")
|
|
1084
|
-
: SURFACE_VIEWS;
|
|
1229
|
+
const surfaceViews = surfaceViewsForScope(cwd !== undefined);
|
|
1085
1230
|
const catalog = useMemo(() => {
|
|
1086
1231
|
const sourceSettings = settingsQuery.data?.available
|
|
1087
1232
|
? settingsQuery.data.settings
|
|
@@ -1271,6 +1416,10 @@ function OmpConfigContent({
|
|
|
1271
1416
|
<ProviderHealthSection theme={theme} styles={styles} cwd={cwd} store={store} />
|
|
1272
1417
|
) : null}
|
|
1273
1418
|
|
|
1419
|
+
{view === "help" ? (
|
|
1420
|
+
<SupportDiagnosticsSection theme={theme} styles={styles} cwd={cwd} store={store} />
|
|
1421
|
+
) : null}
|
|
1422
|
+
|
|
1274
1423
|
{view === "configuration" ? (
|
|
1275
1424
|
<>
|
|
1276
1425
|
<View style={styles.sectionHeader}>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type OmpConfigSurfaceView =
|
|
2
|
+
| "overview"
|
|
3
|
+
| "plugin"
|
|
4
|
+
| "plugins"
|
|
5
|
+
| "composer"
|
|
6
|
+
| "configuration"
|
|
7
|
+
| "diagnostics"
|
|
8
|
+
| "help";
|
|
9
|
+
|
|
10
|
+
const SURFACE_VIEWS: readonly { id: OmpConfigSurfaceView; label: string }[] = [
|
|
11
|
+
{ id: "overview", label: "Overview" },
|
|
12
|
+
{ id: "plugin", label: "Plugin" },
|
|
13
|
+
{ id: "plugins", label: "OMP plugins" },
|
|
14
|
+
{ id: "composer", label: "Composer" },
|
|
15
|
+
{ id: "configuration", label: "Configuration" },
|
|
16
|
+
{ id: "diagnostics", label: "Diagnostics" },
|
|
17
|
+
{ id: "help", label: "Help" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export function surfaceViewsForScope(workspaceScoped: boolean) {
|
|
21
|
+
return workspaceScoped
|
|
22
|
+
? SURFACE_VIEWS.filter((candidate) => candidate.id !== "composer")
|
|
23
|
+
: SURFACE_VIEWS;
|
|
24
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { OmpStore } from "../shared/omp-store";
|
|
2
|
+
|
|
3
|
+
type SupportReportInput = { store?: OmpStore; cwd?: string; force?: boolean };
|
|
4
|
+
type SupportReportOutput = { report: string };
|
|
5
|
+
|
|
6
|
+
export async function refreshSupportReport(
|
|
7
|
+
loadReport: (input: SupportReportInput) => Promise<SupportReportOutput>,
|
|
8
|
+
input: Omit<SupportReportInput, "force">,
|
|
9
|
+
): Promise<SupportReportOutput> {
|
|
10
|
+
return await loadReport({ ...input, force: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type SupportReportCopyState = "idle" | "copying" | "copied" | "error";
|
|
14
|
+
|
|
15
|
+
export interface SupportDiagnosticsViewState {
|
|
16
|
+
refreshLabel: string;
|
|
17
|
+
copyLabel: string;
|
|
18
|
+
copyDisabled: boolean;
|
|
19
|
+
loadingMessage: string | null;
|
|
20
|
+
reportError: string | null;
|
|
21
|
+
copyFeedback: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Maps request and copy state to the exact accessible feedback rendered by the Help tab. */
|
|
25
|
+
export function supportDiagnosticsViewState(input: {
|
|
26
|
+
loading: boolean;
|
|
27
|
+
refreshing: boolean;
|
|
28
|
+
hasReport: boolean;
|
|
29
|
+
reportFailed: boolean;
|
|
30
|
+
copyState: SupportReportCopyState;
|
|
31
|
+
}): SupportDiagnosticsViewState {
|
|
32
|
+
return {
|
|
33
|
+
refreshLabel: input.refreshing ? "Refreshing…" : "Refresh",
|
|
34
|
+
copyLabel: input.copyState === "copying" ? "Copying…" : "Copy report",
|
|
35
|
+
copyDisabled: !input.hasReport || input.copyState === "copying",
|
|
36
|
+
loadingMessage: input.loading ? "Collecting OMP diagnostics…" : null,
|
|
37
|
+
reportError: input.reportFailed ? "Could not collect OMP diagnostics." : null,
|
|
38
|
+
copyFeedback:
|
|
39
|
+
input.copyState === "copied"
|
|
40
|
+
? "Report copied."
|
|
41
|
+
: input.copyState === "error"
|
|
42
|
+
? "Could not copy. Select the report text and copy it manually."
|
|
43
|
+
: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -7,7 +7,7 @@ This checklist prepares `paseo-omp-v0.1.0-alpha.1`. It does not authorize public
|
|
|
7
7
|
- [ ] Release Please proposes `0.1.0-alpha.1` from manifest version `0.0.0`.
|
|
8
8
|
- [ ] Package and tag names are `@omercnet/paseo-omp` and `paseo-omp-v0.1.0-alpha.1`.
|
|
9
9
|
- [ ] Provider identity remains `omp-plugin`; bundled `omp` remains independent and enabled or disabled by the user.
|
|
10
|
-
- [ ] Paseo requirement remains the reviewed
|
|
10
|
+
- [ ] Paseo requirement remains the reviewed range, currently `>=0.9.0-beta.1 <0.10.0`.
|
|
11
11
|
- [ ] Minimum tested OMP version, checksum, CI job, README, SUPPORT, and TESTING agree.
|
|
12
12
|
|
|
13
13
|
## Required gates
|
package/docs/installation.md
CHANGED
|
@@ -4,7 +4,7 @@ Paseo plugins are trusted, unsandboxed code. Review this plugin and its producti
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
- Paseo daemon and apps: `>=0.
|
|
7
|
+
- Paseo daemon and apps: `>=0.9.0-beta.1 <0.10.0`; the 0.9 floor provides the supported plugin copy and external-link APIs, nested provider-subagent ancestry, and spawning-tool links
|
|
8
8
|
- OMP: `18.1.15` or newer is the supported floor
|
|
9
9
|
- OMP RPC: protocol v2 must negotiate successfully
|
|
10
10
|
|
|
@@ -26,9 +26,9 @@ paseo plugin update paseo-omp
|
|
|
26
26
|
paseo plugin update paseo-omp --version <new-version>
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The daemon uses its own npm registry and authentication configuration. npm acquisition installs the artifact's production dependencies before preparation; because npm artifacts omit `package-lock.json`, the preparation helper leaves that tree unchanged. A Git checkout with the committed lockfile runs frozen `npm ci --omit=dev --ignore-scripts`. A failed download, preparation, compatibility check, or activation keeps the installed revision active.
|
|
29
|
+
The daemon uses its own npm registry and authentication configuration. npm acquisition installs the artifact's production dependencies before preparation; because npm artifacts omit `package-lock.json`, the preparation helper leaves that tree unchanged. A Git checkout with the committed lockfile runs frozen `npm ci --omit=dev --ignore-scripts`. A failed download, preparation, compatibility check, or activation keeps the installed revision active.
|
|
30
30
|
|
|
31
|
-
## Install a Git release on Paseo 0.
|
|
31
|
+
## Install a Git release on Paseo 0.9
|
|
32
32
|
|
|
33
33
|
Install the matching reviewed Git tag:
|
|
34
34
|
|
|
@@ -48,17 +48,6 @@ paseo plugin update paseo-omp --ref paseo-omp-v<new-version>
|
|
|
48
48
|
|
|
49
49
|
The install-time `--ref` does not constrain later updates. Paseo 0.9 stages and validates the selected revision before replacing the active installation, so plugin settings remain intact and a failed update leaves the previous revision running.
|
|
50
50
|
|
|
51
|
-
### Update a tag-pinned Git installation on Paseo 0.8
|
|
52
|
-
|
|
53
|
-
Paseo 0.8 does not provide the 0.9 non-destructive explicit-ref update flow. Record the current installation, then replace it with the new tag in one maintenance window:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
paseo plugin ls paseo-omp --json > paseo-omp-before-update.json
|
|
57
|
-
paseo plugin remove paseo-omp
|
|
58
|
-
paseo plugin add omercnet/paseo-plugins:paseo-omp --ref paseo-omp-v<new-version>
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Removal deletes plugin-scoped settings and briefly makes `omp-plugin` unavailable. It does not modify Paseo's bundled `omp` provider or native OMP transcripts. Roll back by repeating the remove/add sequence with the recorded tag or commit.
|
|
62
51
|
|
|
63
52
|
## Install a local checkout
|
|
64
53
|
|
|
@@ -86,4 +75,4 @@ Tracking `main` executes future dependency and plugin updates with the daemon us
|
|
|
86
75
|
paseo plugin add omercnet/paseo-plugins:paseo-omp --ref main
|
|
87
76
|
```
|
|
88
77
|
|
|
89
|
-
On Paseo 0.9, preview and approve the remote default HEAD with `paseo plugin update paseo-omp`, or select `main` explicitly with `paseo plugin update paseo-omp --ref main`.
|
|
78
|
+
On Paseo 0.9, preview and approve the remote default HEAD with `paseo plugin update paseo-omp`, or select `main` explicitly with `paseo plugin update paseo-omp --ref main`. A failed build or compatibility check leaves the previous revision active.
|
package/index.server.ts
CHANGED
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
resolveMutateOmpPluginConfig,
|
|
15
15
|
} from "./server/omp-plugins";
|
|
16
16
|
import { resolveListOmpSettings, resolveUpdateOmpSettings } from "./server/omp-settings";
|
|
17
|
+
import { OmpOperationalFailureCollector } from "./server/operational-failure-diagnostics";
|
|
17
18
|
import { withOmpStore } from "./server/paths";
|
|
19
|
+
import { OmpProtocolViolationCollector } from "./server/protocol-violation-diagnostics";
|
|
18
20
|
import { withOmpWorkspaceIdentity } from "./server/provider/host-tools";
|
|
19
21
|
import {
|
|
20
22
|
createProfileOmpProvider,
|
|
@@ -25,6 +27,7 @@ import { createOmpProvider } from "./server/provider/registration";
|
|
|
25
27
|
import { resolveGetOmpProviderHealth } from "./server/provider-diagnostics";
|
|
26
28
|
import { resolveListOmpQuotas } from "./server/quota";
|
|
27
29
|
import { resolveListOmpSessions } from "./server/sessions";
|
|
30
|
+
import { resolveGetOmpSupportReport } from "./server/support-diagnostics";
|
|
28
31
|
import { composerPillSettings } from "./shared/composer-pill-settings";
|
|
29
32
|
import { listHubProcesses, tailHubLog } from "./shared/hub";
|
|
30
33
|
import { openOmpMcpAuthorizationInPaseoBrowser } from "./shared/mcp";
|
|
@@ -42,6 +45,7 @@ import { listOmpStores, type OmpStore } from "./shared/omp-store";
|
|
|
42
45
|
import { getOmpProviderHealth } from "./shared/provider-diagnostics";
|
|
43
46
|
import { listOmpQuotas } from "./shared/quota";
|
|
44
47
|
import { listOmpSessions } from "./shared/sessions";
|
|
48
|
+
import { getOmpSupportReport } from "./shared/support-diagnostics";
|
|
45
49
|
|
|
46
50
|
function scoped<T extends { store?: OmpStore }, R>(handler: (input: T) => R) {
|
|
47
51
|
return (input: T): R => withOmpStore(input.store, () => handler(input));
|
|
@@ -50,10 +54,18 @@ function scoped<T extends { store?: OmpStore }, R>(handler: (input: T) => R) {
|
|
|
50
54
|
export default function contribute(server: PluginServerContext) {
|
|
51
55
|
server.registerSettings(composerPillSettings);
|
|
52
56
|
const browserAuthorizationRegistry = new OmpBrowserAuthorizationRegistry();
|
|
57
|
+
const protocolViolations = new OmpProtocolViolationCollector();
|
|
58
|
+
const operationalFailures = new OmpOperationalFailureCollector();
|
|
53
59
|
const profiles = discoverOmpProfilesSync();
|
|
54
60
|
server.handle(listOmpStores, async () => ({ profiles: await discoverOmpProfiles() }));
|
|
55
61
|
for (const profile of profiles) {
|
|
56
|
-
server.registerProvider(
|
|
62
|
+
server.registerProvider(
|
|
63
|
+
createProfileOmpProvider(profile, {
|
|
64
|
+
browserAuthorizationRegistry,
|
|
65
|
+
reportProtocolViolation: protocolViolations.report,
|
|
66
|
+
reportOperationalFailure: operationalFailures.report,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
57
69
|
}
|
|
58
70
|
server.handle(listHubProcesses, resolveListHubProcesses);
|
|
59
71
|
server.handle(tailHubLog, resolveTailHubLog);
|
|
@@ -69,6 +81,10 @@ export default function contribute(server: PluginServerContext) {
|
|
|
69
81
|
server.handle(listOmpSettings, scoped(resolveListOmpSettings));
|
|
70
82
|
server.handle(updateOmpSettings, scoped(resolveUpdateOmpSettings));
|
|
71
83
|
server.handle(getOmpProviderHealth, scoped(resolveGetOmpProviderHealth));
|
|
84
|
+
server.handle(
|
|
85
|
+
getOmpSupportReport,
|
|
86
|
+
scoped((input) => resolveGetOmpSupportReport(input, protocolViolations, operationalFailures)),
|
|
87
|
+
);
|
|
72
88
|
server.handle(openOmpMcpAuthorizationInPaseoBrowser, (input) =>
|
|
73
89
|
resolveOpenOmpMcpAuthorizationInPaseoBrowser(input, browserAuthorizationRegistry),
|
|
74
90
|
);
|
|
@@ -76,7 +92,13 @@ export default function contribute(server: PluginServerContext) {
|
|
|
76
92
|
if (request.provider !== "omp-plugin" && !request.provider.startsWith("omp-plugin-")) return;
|
|
77
93
|
return withOmpWorkspaceIdentity(request);
|
|
78
94
|
});
|
|
79
|
-
server.registerProvider(
|
|
95
|
+
server.registerProvider(
|
|
96
|
+
createOmpProvider({
|
|
97
|
+
browserAuthorizationRegistry,
|
|
98
|
+
reportProtocolViolation: protocolViolations.report,
|
|
99
|
+
reportOperationalFailure: operationalFailures.report,
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
80
102
|
return () => {
|
|
81
103
|
browserAuthorizationRegistry.clear();
|
|
82
104
|
removeIdentityHook();
|
package/package.json
CHANGED
package/paseo-plugin.json
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const OMP_OPERATIONAL_FAILURES = [
|
|
2
|
+
{ category: "session-open", stage: "startup" },
|
|
3
|
+
{ category: "session-open", stage: "catalog" },
|
|
4
|
+
{ category: "replay-recovery", stage: "persisted-replay" },
|
|
5
|
+
{ category: "replay-recovery", stage: "runtime-recovery" },
|
|
6
|
+
{ category: "replay-recovery", stage: "rewind" },
|
|
7
|
+
{ category: "tool-projector", stage: "host-tool-unknown" },
|
|
8
|
+
{ category: "tool-projector", stage: "host-tool-capacity" },
|
|
9
|
+
{ category: "tool-projector", stage: "host-tool-normalization" },
|
|
10
|
+
{ category: "tool-projector", stage: "host-tool-call" },
|
|
11
|
+
{ category: "tool-projector", stage: "host-tool-timeout" },
|
|
12
|
+
{ category: "tool-projector", stage: "host-tool-delivery" },
|
|
13
|
+
{ category: "tool-projector", stage: "timeline-projector" },
|
|
14
|
+
{ category: "tool-projector", stage: "subsession-projector" },
|
|
15
|
+
{ category: "terminal-outcome", stage: "failed" },
|
|
16
|
+
{ category: "terminal-outcome", stage: "unresolved" },
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export type OmpOperationalFailure = (typeof OMP_OPERATIONAL_FAILURES)[number];
|
|
20
|
+
export type OmpOperationalFailureCategory = OmpOperationalFailure["category"];
|
|
21
|
+
export type OmpOperationalFailureStage = OmpOperationalFailure["stage"];
|
|
22
|
+
export type OmpOperationalFailureReporter = (failure: OmpOperationalFailure) => void;
|
|
23
|
+
|
|
24
|
+
export type OmpOperationalFailureSummary = OmpOperationalFailure & {
|
|
25
|
+
occurrenceCount: number;
|
|
26
|
+
firstAt: string | null;
|
|
27
|
+
lastAt: string | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const FAILURE_KEYS: Readonly<Record<string, true>> = Object.fromEntries(
|
|
31
|
+
OMP_OPERATIONAL_FAILURES.map(({ category, stage }) => [`${category}:${stage}`, true]),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
function timestamp(now: () => Date): string | null {
|
|
35
|
+
try {
|
|
36
|
+
const value = now();
|
|
37
|
+
return Number.isFinite(value.getTime()) ? value.toISOString() : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Fixed category/stage cells make aggregation constant-space for one plugin subprocess lifetime. */
|
|
44
|
+
export class OmpOperationalFailureCollector {
|
|
45
|
+
private readonly summaries = Object.fromEntries(
|
|
46
|
+
OMP_OPERATIONAL_FAILURES.map(({ category, stage }) => [
|
|
47
|
+
`${category}:${stage}`,
|
|
48
|
+
{ category, stage, occurrenceCount: 0, firstAt: null, lastAt: null },
|
|
49
|
+
]),
|
|
50
|
+
) as Record<string, OmpOperationalFailureSummary>;
|
|
51
|
+
|
|
52
|
+
constructor(private readonly now: () => Date = () => new Date()) {}
|
|
53
|
+
|
|
54
|
+
report: OmpOperationalFailureReporter = (failure) => {
|
|
55
|
+
try {
|
|
56
|
+
const key = `${failure.category}:${failure.stage}`;
|
|
57
|
+
if (!FAILURE_KEYS[key]) return;
|
|
58
|
+
const summary = this.summaries[key];
|
|
59
|
+
if (!summary) return;
|
|
60
|
+
summary.occurrenceCount = Math.min(Number.MAX_SAFE_INTEGER, summary.occurrenceCount + 1);
|
|
61
|
+
const recordedAt = timestamp(this.now);
|
|
62
|
+
if (recordedAt) {
|
|
63
|
+
summary.firstAt ??= recordedAt;
|
|
64
|
+
summary.lastAt = recordedAt;
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// Diagnostics must never affect provider or session flow.
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
snapshot(): OmpOperationalFailureSummary[] {
|
|
72
|
+
return OMP_OPERATIONAL_FAILURES.map(({ category, stage }) => ({
|
|
73
|
+
...this.summaries[`${category}:${stage}`],
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
}
|